mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
677 lines
20 KiB
Zig
677 lines
20 KiB
Zig
const std = @import("std");
|
|
|
|
const main = @import("root");
|
|
const graphics = main.graphics;
|
|
const draw = graphics.draw;
|
|
const JsonElement = main.JsonElement;
|
|
const settings = main.settings;
|
|
const vec = main.vec;
|
|
const Vec2f = vec.Vec2f;
|
|
const List = main.List;
|
|
|
|
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
|
|
|
|
const Button = @import("components/Button.zig");
|
|
const CheckBox = @import("components/CheckBox.zig");
|
|
const ItemSlot = @import("components/ItemSlot.zig");
|
|
const ScrollBar = @import("components/ScrollBar.zig");
|
|
const ContinuousSlider = @import("components/ContinuousSlider.zig");
|
|
const DiscreteSlider = @import("components/DiscreteSlider.zig");
|
|
const TextInput = @import("components/TextInput.zig");
|
|
pub const GuiComponent = @import("gui_component.zig").GuiComponent;
|
|
pub const GuiWindow = @import("GuiWindow.zig");
|
|
|
|
pub const windowlist = @import("windows/_windowlist.zig");
|
|
|
|
var windowList: List(*GuiWindow) = undefined;
|
|
var hudWindows: List(*GuiWindow) = undefined;
|
|
pub var openWindows: List(*GuiWindow) = undefined;
|
|
var selectedWindow: ?*GuiWindow = null;
|
|
var selectedTextInput: ?*TextInput = null;
|
|
var hoveredAWindow: bool = false;
|
|
|
|
pub var scale: f32 = undefined;
|
|
|
|
pub var hoveredItemSlot: ?*ItemSlot = null;
|
|
|
|
const GuiCommandQueue = struct {
|
|
const Action = enum {
|
|
open,
|
|
close,
|
|
};
|
|
const Command = struct {
|
|
window: *GuiWindow,
|
|
action: Action,
|
|
};
|
|
|
|
var commands: List(Command) = undefined;
|
|
var mutex: std.Thread.Mutex = .{};
|
|
|
|
fn init() void {
|
|
mutex.lock();
|
|
defer mutex.unlock();
|
|
commands = List(Command).init(main.globalAllocator);
|
|
}
|
|
|
|
fn deinit() void {
|
|
mutex.lock();
|
|
defer mutex.unlock();
|
|
commands.deinit();
|
|
}
|
|
|
|
fn scheduleCommand(command: Command) void {
|
|
mutex.lock();
|
|
defer mutex.unlock();
|
|
commands.append(command);
|
|
}
|
|
|
|
fn executeCommands() void {
|
|
mutex.lock();
|
|
defer mutex.unlock();
|
|
for(commands.items) |command| {
|
|
switch(command.action) {
|
|
.open => {
|
|
executeOpenWindowCommand(command.window);
|
|
},
|
|
.close => {
|
|
executeCloseWindowCommand(command.window);
|
|
}
|
|
}
|
|
}
|
|
commands.clearRetainingCapacity();
|
|
}
|
|
|
|
fn executeOpenWindowCommand(window: *GuiWindow) void {
|
|
std.debug.assert(!mutex.tryLock()); // mutex must be locked.
|
|
defer updateWindowPositions();
|
|
for(openWindows.items, 0..) |_openWindow, i| {
|
|
if(_openWindow == window) {
|
|
_ = openWindows.swapRemove(i);
|
|
openWindows.appendAssumeCapacity(window);
|
|
selectedWindow = null;
|
|
return;
|
|
}
|
|
}
|
|
openWindows.append(window);
|
|
window.onOpenFn();
|
|
selectedWindow = null;
|
|
}
|
|
|
|
fn executeCloseWindowCommand(window: *GuiWindow) void {
|
|
std.debug.assert(!mutex.tryLock()); // mutex must be locked.
|
|
defer updateWindowPositions();
|
|
if(selectedWindow == window) {
|
|
selectedWindow = null;
|
|
}
|
|
for(openWindows.items, 0..) |_openWindow, i| {
|
|
if(_openWindow == window) {
|
|
_ = openWindows.swapRemove(i);
|
|
break;
|
|
}
|
|
}
|
|
window.onCloseFn();
|
|
}
|
|
};
|
|
|
|
pub const Callback = struct {
|
|
callback: ?*const fn(usize) void = null,
|
|
arg: usize = 0,
|
|
|
|
pub fn run(self: Callback) void {
|
|
if(self.callback) |callback| {
|
|
callback(self.arg);
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn init() void {
|
|
GuiCommandQueue.init();
|
|
windowList = List(*GuiWindow).init(main.globalAllocator);
|
|
hudWindows = List(*GuiWindow).init(main.globalAllocator);
|
|
openWindows = List(*GuiWindow).init(main.globalAllocator);
|
|
inline for(@typeInfo(windowlist).Struct.decls) |decl| {
|
|
const windowStruct = @field(windowlist, decl.name);
|
|
std.debug.assert(std.mem.eql(u8, decl.name, windowStruct.window.id)); // id and file name should be the same.
|
|
addWindow(&windowStruct.window);
|
|
if(@hasDecl(windowStruct, "init")) {
|
|
windowStruct.init();
|
|
}
|
|
const functionNames = [_][]const u8{"render", "update", "updateSelected", "updateHovered", "onOpen", "onClose"};
|
|
inline for(functionNames) |function| {
|
|
if(@hasDecl(windowStruct, function)) {
|
|
@field(windowStruct.window, function ++ "Fn") = &@field(windowStruct, function);
|
|
}
|
|
}
|
|
}
|
|
GuiWindow.__init();
|
|
Button.__init();
|
|
CheckBox.__init();
|
|
ItemSlot.__init();
|
|
ScrollBar.__init();
|
|
ContinuousSlider.__init();
|
|
DiscreteSlider.__init();
|
|
TextInput.__init();
|
|
load();
|
|
inventory.init();
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
save();
|
|
windowList.deinit();
|
|
hudWindows.deinit();
|
|
for(openWindows.items) |window| {
|
|
window.onCloseFn();
|
|
}
|
|
openWindows.deinit();
|
|
GuiWindow.__deinit();
|
|
Button.__deinit();
|
|
CheckBox.__deinit();
|
|
ItemSlot.__deinit();
|
|
ScrollBar.__deinit();
|
|
ContinuousSlider.__deinit();
|
|
DiscreteSlider.__deinit();
|
|
TextInput.__deinit();
|
|
inline for(@typeInfo(windowlist).Struct.decls) |decl| {
|
|
const WindowStruct = @field(windowlist, decl.name);
|
|
if(@hasDecl(WindowStruct, "deinit")) {
|
|
WindowStruct.deinit();
|
|
}
|
|
}
|
|
inventory.deinit();
|
|
GuiCommandQueue.deinit();
|
|
}
|
|
|
|
fn save() void {
|
|
const guiJson = JsonElement.initObject(main.globalAllocator);
|
|
defer guiJson.free(main.globalAllocator);
|
|
for(windowList.items) |window| {
|
|
const windowJson = JsonElement.initObject(main.globalAllocator);
|
|
for(window.relativePosition, 0..) |relPos, i| {
|
|
const relPosJson = JsonElement.initObject(main.globalAllocator);
|
|
switch(relPos) {
|
|
.ratio => |ratio| {
|
|
relPosJson.put("type", "ratio");
|
|
relPosJson.put("ratio", ratio);
|
|
},
|
|
.attachedToFrame => |attachedToFrame| {
|
|
relPosJson.put("type", "attachedToFrame");
|
|
relPosJson.put("selfAttachmentPoint", @intFromEnum(attachedToFrame.selfAttachmentPoint));
|
|
relPosJson.put("otherAttachmentPoint", @intFromEnum(attachedToFrame.otherAttachmentPoint));
|
|
},
|
|
.relativeToWindow => |relativeToWindow| {
|
|
relPosJson.put("type", "relativeToWindow");
|
|
relPosJson.put("reference", relativeToWindow.reference.id);
|
|
relPosJson.put("ratio", relativeToWindow.ratio);
|
|
},
|
|
.attachedToWindow => |attachedToWindow| {
|
|
relPosJson.put("type", "attachedToWindow");
|
|
relPosJson.put("reference", attachedToWindow.reference.id);
|
|
relPosJson.put("selfAttachmentPoint", @intFromEnum(attachedToWindow.selfAttachmentPoint));
|
|
relPosJson.put("otherAttachmentPoint", @intFromEnum(attachedToWindow.otherAttachmentPoint));
|
|
},
|
|
}
|
|
windowJson.put(([_][]const u8{"relPos0", "relPos1"})[i], relPosJson);
|
|
}
|
|
windowJson.put("scale", window.scale);
|
|
guiJson.put(window.id, windowJson);
|
|
}
|
|
|
|
main.files.writeJson("gui_layout.json", guiJson) catch |err| {
|
|
std.log.err("Could not write gui_layout.json: {s}", .{@errorName(err)});
|
|
};
|
|
}
|
|
|
|
fn load() void {
|
|
const json: JsonElement = main.files.readToJson(main.globalAllocator, "gui_layout.json") catch |err| blk: {
|
|
if(err != error.FileNotFound) {
|
|
std.log.err("Could not read gui_layout.json: {s}", .{@errorName(err)});
|
|
}
|
|
break :blk JsonElement{.JsonNull={}};
|
|
};
|
|
defer json.free(main.globalAllocator);
|
|
|
|
for(windowList.items) |window| {
|
|
const windowJson = json.getChild(window.id);
|
|
if(windowJson == .JsonNull) continue;
|
|
for(&window.relativePosition, 0..) |*relPos, i| {
|
|
const relPosJson = windowJson.getChild(([_][]const u8{"relPos0", "relPos1"})[i]);
|
|
const typ = relPosJson.get([]const u8, "type", "ratio");
|
|
if(std.mem.eql(u8, typ, "ratio")) {
|
|
relPos.* = .{.ratio = relPosJson.get(f32, "ratio", 0.5)};
|
|
} else if(std.mem.eql(u8, typ, "attachedToFrame")) {
|
|
relPos.* = .{.attachedToFrame = .{
|
|
.selfAttachmentPoint = @enumFromInt(relPosJson.get(u8, "selfAttachmentPoint", 0)),
|
|
.otherAttachmentPoint = @enumFromInt(relPosJson.get(u8, "otherAttachmentPoint", 0)),
|
|
}};
|
|
} else if(std.mem.eql(u8, typ, "relativeToWindow")) {
|
|
const reference = getWindowById(relPosJson.get([]const u8, "reference", "")) orelse continue;
|
|
relPos.* = .{.relativeToWindow = .{
|
|
.reference = reference,
|
|
.ratio = relPosJson.get(f32, "ratio", 0.5),
|
|
}};
|
|
} else if(std.mem.eql(u8, typ, "attachedToWindow")) {
|
|
const reference = getWindowById(relPosJson.get([]const u8, "reference", "")) orelse continue;
|
|
relPos.* = .{.attachedToWindow = .{
|
|
.reference = reference,
|
|
.selfAttachmentPoint = @enumFromInt(relPosJson.get(u8, "selfAttachmentPoint", 0)),
|
|
.otherAttachmentPoint = @enumFromInt(relPosJson.get(u8, "otherAttachmentPoint", 0)),
|
|
}};
|
|
} else {
|
|
std.log.warn("Unknown window attachment type: {s}", .{typ});
|
|
}
|
|
}
|
|
window.scale = windowJson.get(f32, "scale", 1);
|
|
}
|
|
}
|
|
|
|
fn getWindowById(id: []const u8) ?*GuiWindow {
|
|
for(windowList.items) |window| {
|
|
if(std.mem.eql(u8, id, window.id)) {
|
|
return window;
|
|
}
|
|
}
|
|
std.log.warn("Could not find window with id: {s}", .{id});
|
|
return null;
|
|
}
|
|
|
|
pub fn updateGuiScale() void {
|
|
if(settings.guiScale) |guiScale| {
|
|
scale = guiScale;
|
|
} else {
|
|
const windowSize = main.Window.getWindowSize();
|
|
const screenWidth = @min(windowSize[0], windowSize[1]*16/9);
|
|
scale = @floor(screenWidth/640.0 + 0.2);
|
|
if(scale < 1) {
|
|
scale = 0.5;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn addWindow(window: *GuiWindow) void {
|
|
for(windowList.items) |other| {
|
|
if(std.mem.eql(u8, window.id, other.id)) {
|
|
std.log.err("Duplicate window id: {s}", .{window.id});
|
|
return;
|
|
}
|
|
}
|
|
if(window.isHud) {
|
|
hudWindows.append(window);
|
|
}
|
|
windowList.append(window);
|
|
}
|
|
|
|
pub fn openWindow(id: []const u8) void {
|
|
defer updateWindowPositions();
|
|
for(windowList.items) |window| {
|
|
if(std.mem.eql(u8, window.id, id)) {
|
|
openWindowFromRef(window);
|
|
return;
|
|
}
|
|
}
|
|
std.log.warn("Could not find window with id {s}.", .{id});
|
|
}
|
|
|
|
pub fn openWindowFromRef(window: *GuiWindow) void {
|
|
GuiCommandQueue.scheduleCommand(.{.action = .open, .window = window});
|
|
}
|
|
|
|
pub fn toggleWindow(id: []const u8) void {
|
|
defer updateWindowPositions();
|
|
for(windowList.items) |window| {
|
|
if(std.mem.eql(u8, window.id, id)) {
|
|
for(openWindows.items, 0..) |_openWindow, i| {
|
|
if(_openWindow == window) {
|
|
_ = openWindows.swapRemove(i);
|
|
selectedWindow = null;
|
|
return;
|
|
}
|
|
}
|
|
openWindows.append(window);
|
|
window.onOpenFn();
|
|
selectedWindow = null;
|
|
return;
|
|
}
|
|
}
|
|
std.log.warn("Could not find window with id {s}.", .{id});
|
|
}
|
|
|
|
pub fn openHud() void {
|
|
for(windowList.items) |window| {
|
|
if(window.isHud) {
|
|
openWindowFromRef(window);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn openWindowCallbackFunction(windowPtr: usize) void {
|
|
openWindowFromRef(@ptrFromInt(windowPtr));
|
|
}
|
|
pub fn openWindowCallback(comptime id: []const u8) Callback {
|
|
return .{
|
|
.callback = &openWindowCallbackFunction,
|
|
.arg = @intFromPtr(&@field(windowlist, id).window),
|
|
};
|
|
}
|
|
|
|
pub fn closeWindow(window: *GuiWindow) void {
|
|
GuiCommandQueue.scheduleCommand(.{.action = .close, .window = window});
|
|
}
|
|
|
|
pub fn setSelectedTextInput(newSelectedTextInput: ?*TextInput) void {
|
|
if(selectedTextInput) |current| {
|
|
if(current != newSelectedTextInput) {
|
|
current.deselect();
|
|
}
|
|
}
|
|
selectedTextInput = newSelectedTextInput;
|
|
}
|
|
|
|
pub const textCallbacks = struct {
|
|
pub fn char(codepoint: u21) void {
|
|
if(selectedTextInput) |current| {
|
|
current.inputCharacter(codepoint);
|
|
}
|
|
}
|
|
pub fn left(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.left(mods);
|
|
}
|
|
}
|
|
pub fn right(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.right(mods);
|
|
}
|
|
}
|
|
pub fn down(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.down(mods);
|
|
}
|
|
}
|
|
pub fn up(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.up(mods);
|
|
}
|
|
}
|
|
pub fn gotoStart(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.gotoStart(mods);
|
|
}
|
|
}
|
|
pub fn gotoEnd(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.gotoEnd(mods);
|
|
}
|
|
}
|
|
pub fn deleteLeft(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.deleteLeft(mods);
|
|
}
|
|
}
|
|
pub fn deleteRight(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.deleteRight(mods);
|
|
}
|
|
}
|
|
pub fn copy(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.copy(mods);
|
|
}
|
|
}
|
|
pub fn paste(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.paste(mods);
|
|
}
|
|
}
|
|
pub fn cut(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.cut(mods);
|
|
}
|
|
}
|
|
pub fn newline(mods: main.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.newline(mods);
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn mainButtonPressed() void {
|
|
if(main.Window.grabbed) return;
|
|
inventory.update();
|
|
selectedWindow = null;
|
|
selectedTextInput = null;
|
|
var selectedI: usize = 0;
|
|
for(openWindows.items, 0..) |window, i| {
|
|
var mousePosition = main.Window.getMousePosition()/@as(Vec2f, @splat(scale));
|
|
mousePosition -= window.pos;
|
|
if(@reduce(.And, mousePosition >= Vec2f{0, 0}) and @reduce(.And, mousePosition < window.size)) {
|
|
selectedWindow = window;
|
|
selectedI = i;
|
|
}
|
|
}
|
|
if(selectedWindow) |_selectedWindow| {
|
|
const mousePosition = main.Window.getMousePosition()/@as(Vec2f, @splat(scale));
|
|
_selectedWindow.mainButtonPressed(mousePosition);
|
|
_ = openWindows.orderedRemove(selectedI);
|
|
openWindows.appendAssumeCapacity(_selectedWindow);
|
|
} else if(main.game.world != null) {
|
|
main.Window.setMouseGrabbed(true);
|
|
}
|
|
}
|
|
|
|
pub fn mainButtonReleased() void {
|
|
if(main.Window.grabbed) return;
|
|
inventory.applyChanges(true);
|
|
const oldWindow = selectedWindow;
|
|
selectedWindow = null;
|
|
for(openWindows.items) |window| {
|
|
var mousePosition = main.Window.getMousePosition()/@as(Vec2f, @splat(scale));
|
|
mousePosition -= window.pos;
|
|
if(@reduce(.And, mousePosition >= Vec2f{0, 0}) and @reduce(.And, mousePosition < window.size)) {
|
|
selectedWindow = window;
|
|
}
|
|
}
|
|
if(selectedWindow != oldWindow) { // Unselect the window if the mouse left it.
|
|
selectedWindow = null;
|
|
}
|
|
if(oldWindow) |_oldWindow| {
|
|
const mousePosition = main.Window.getMousePosition()/@as(Vec2f, @splat(scale));
|
|
_oldWindow.mainButtonReleased(mousePosition);
|
|
}
|
|
}
|
|
|
|
pub fn secondaryButtonPressed() void {
|
|
if(main.Window.grabbed) return;
|
|
inventory.update();
|
|
if(inventory.carriedItemStack.amount != 0) return;
|
|
}
|
|
|
|
pub fn secondaryButtonReleased() void {
|
|
if(main.Window.grabbed) return;
|
|
inventory.applyChanges(false);
|
|
}
|
|
|
|
pub fn updateWindowPositions() void {
|
|
var wasChanged: bool = false;
|
|
for(windowList.items) |window| {
|
|
const oldPos = window.pos;
|
|
window.updateWindowPosition();
|
|
const newPos = window.pos;
|
|
if(vec.lengthSquare(oldPos - newPos) >= 1e-3) {
|
|
wasChanged = true;
|
|
}
|
|
}
|
|
if(wasChanged) @call(.always_tail, updateWindowPositions, .{}); // Very efficient O(n²) algorithm :P
|
|
}
|
|
|
|
pub fn updateAndRenderGui() void {
|
|
const mousePos = main.Window.getMousePosition()/@as(Vec2f, @splat(scale));
|
|
hoveredAWindow = false;
|
|
GuiCommandQueue.executeCommands();
|
|
if(!main.Window.grabbed) {
|
|
if(selectedWindow) |selected| {
|
|
selected.updateSelected(mousePos);
|
|
}
|
|
hoveredItemSlot = null;
|
|
var i: usize = openWindows.items.len;
|
|
while(i != 0) {
|
|
i -= 1;
|
|
const window: *GuiWindow = openWindows.items[i];
|
|
if(GuiComponent.contains(window.pos, window.size, mousePos)) {
|
|
window.updateHovered(mousePos);
|
|
hoveredAWindow = true;
|
|
break;
|
|
}
|
|
}
|
|
inventory.update();
|
|
}
|
|
for(openWindows.items) |window| {
|
|
window.update();
|
|
}
|
|
if(!main.Window.grabbed) {
|
|
draw.setColor(0x80000000);
|
|
GuiWindow.borderShader.bind();
|
|
graphics.c.glUniform2f(GuiWindow.borderUniforms.effectLength, main.Window.getWindowSize()[0]/6, main.Window.getWindowSize()[1]/6);
|
|
draw.customShadedRect(GuiWindow.borderUniforms, .{0, 0}, main.Window.getWindowSize());
|
|
}
|
|
const oldScale = draw.setScale(scale);
|
|
defer draw.restoreScale(oldScale);
|
|
for(openWindows.items) |window| {
|
|
window.render(mousePos);
|
|
}
|
|
inventory.render(mousePos);
|
|
}
|
|
|
|
pub const inventory = struct {
|
|
const ItemStack = main.items.ItemStack;
|
|
pub var carriedItemStack: ItemStack = .{.item = null, .amount = 0};
|
|
var carriedItemSlot: *ItemSlot = undefined;
|
|
var deliveredItemSlots: List(*ItemSlot) = undefined;
|
|
var deliveredItemStacksAmountAdded: List(u16) = undefined;
|
|
var initialAmount: u16 = 0;
|
|
|
|
pub fn init() void {
|
|
deliveredItemSlots = List(*ItemSlot).init(main.globalAllocator);
|
|
deliveredItemStacksAmountAdded = List(u16).init(main.globalAllocator);
|
|
carriedItemSlot = ItemSlot.init(.{0, 0}, carriedItemStack, undefined, undefined, .default, .normal);
|
|
carriedItemSlot.renderFrame = false;
|
|
}
|
|
|
|
fn deinit() void {
|
|
carriedItemSlot.deinit();
|
|
deliveredItemSlots.deinit();
|
|
deliveredItemStacksAmountAdded.deinit();
|
|
std.debug.assert(carriedItemStack.amount == 0);
|
|
}
|
|
|
|
fn update() void {
|
|
if(deliveredItemSlots.items.len == 0) {
|
|
initialAmount = carriedItemStack.amount;
|
|
}
|
|
if(hoveredItemSlot) |itemSlot| {
|
|
if(itemSlot.mode != .normal) return;
|
|
if(initialAmount == 0) return;
|
|
if(!std.meta.eql(itemSlot.itemStack.item, carriedItemStack.item) and itemSlot.itemStack.item != null) return;
|
|
|
|
if(main.KeyBoard.key("mainGuiButton").pressed) {
|
|
for(deliveredItemSlots.items) |deliveredSlot| {
|
|
if(itemSlot == deliveredSlot) {
|
|
return;
|
|
}
|
|
}
|
|
for(deliveredItemSlots.items, deliveredItemStacksAmountAdded.items) |deliveredSlot, oldAmountAdded| {
|
|
deliveredSlot.tryTakingItems(&carriedItemStack, oldAmountAdded);
|
|
}
|
|
initialAmount = carriedItemStack.amount;
|
|
deliveredItemSlots.append(itemSlot);
|
|
deliveredItemStacksAmountAdded.append(0);
|
|
carriedItemStack.amount = initialAmount;
|
|
const addedAmount: u16 = @intCast(initialAmount/deliveredItemSlots.items.len);
|
|
for(deliveredItemSlots.items, deliveredItemStacksAmountAdded.items) |deliveredSlot, *amountAdded| {
|
|
const old = carriedItemStack.amount;
|
|
deliveredSlot.tryAddingItems(&carriedItemStack, addedAmount);
|
|
amountAdded.* = old - carriedItemStack.amount;
|
|
}
|
|
} else if(main.KeyBoard.key("secondaryGuiButton").pressed) {
|
|
for(deliveredItemSlots.items) |deliveredStack| {
|
|
if(itemSlot == deliveredStack) {
|
|
return;
|
|
}
|
|
}
|
|
if(carriedItemStack.amount != 0) {
|
|
itemSlot.tryAddingItems(&carriedItemStack, 1);
|
|
deliveredItemSlots.append(itemSlot);
|
|
deliveredItemStacksAmountAdded.append(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn applyChanges(leftClick: bool) void {
|
|
if(main.game.world == null) return;
|
|
if(deliveredItemSlots.items.len != 0) {
|
|
deliveredItemSlots.clearRetainingCapacity();
|
|
deliveredItemStacksAmountAdded.clearRetainingCapacity();
|
|
if(carriedItemStack.amount == 0) {
|
|
carriedItemStack.item = null;
|
|
}
|
|
} else if(hoveredItemSlot) |hovered| {
|
|
if(carriedItemStack.amount != 0) {
|
|
if(leftClick) {
|
|
if(std.meta.eql(carriedItemStack.item, hovered.itemStack.item)) {
|
|
hovered.tryTakingItems(&carriedItemStack, hovered.itemStack.amount);
|
|
} else {
|
|
hovered.trySwappingItems(&carriedItemStack);
|
|
}
|
|
}
|
|
} else {
|
|
if(leftClick) {
|
|
hovered.tryTakingItems(&carriedItemStack, hovered.itemStack.amount);
|
|
} else {
|
|
hovered.tryTakingItems(&carriedItemStack, hovered.itemStack.amount/2);
|
|
}
|
|
}
|
|
} else if(!hoveredAWindow) {
|
|
if(leftClick or carriedItemStack.amount == 1) {
|
|
main.network.Protocols.genericUpdate.itemStackDrop(main.game.world.?.conn, carriedItemStack, @floatCast(main.game.Player.getPosBlocking()), main.game.camera.direction, 20);
|
|
carriedItemStack.clear();
|
|
} else if(carriedItemStack.amount != 0) {
|
|
main.network.Protocols.genericUpdate.itemStackDrop(main.game.world.?.conn, .{.item = carriedItemStack.item, .amount = 1}, @floatCast(main.game.Player.getPosBlocking()), main.game.camera.direction, 20);
|
|
_ = carriedItemStack.add(carriedItemStack.item.?, @as(i32, -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render(mousePos: Vec2f) void {
|
|
carriedItemSlot.updateItemStack(carriedItemStack);
|
|
carriedItemSlot.pos = mousePos - Vec2f{12, 12};
|
|
carriedItemSlot.render(.{0, 0});
|
|
// Draw tooltip:
|
|
if(carriedItemStack.amount == 0) if(hoveredItemSlot) |hovered| {
|
|
if(hovered.itemStack.item) |item| {
|
|
const tooltip = item.getTooltip();
|
|
var textBuffer: graphics.TextBuffer = graphics.TextBuffer.init(main.globalAllocator, tooltip, .{}, false, .left);
|
|
defer textBuffer.deinit();
|
|
var size = textBuffer.calculateLineBreaks(16, 256);
|
|
size[0] = 0;
|
|
for(textBuffer.lineBreaks.items) |lineBreak| {
|
|
size[0] = @max(size[0], lineBreak.width);
|
|
}
|
|
var pos = mousePos;
|
|
if(pos[0] + size[0] >= main.Window.getWindowSize()[0]/scale) {
|
|
pos[0] -= size[0];
|
|
}
|
|
if(pos[1] + size[1] >= main.Window.getWindowSize()[1]/scale) {
|
|
pos[1] -= size[1];
|
|
}
|
|
pos = @max(pos, Vec2f{0, 0});
|
|
const border1: f32 = 2;
|
|
const border2: f32 = 1;
|
|
draw.setColor(0xffffff00);
|
|
draw.rect(pos - @as(Vec2f, @splat(border1)), size + @as(Vec2f, @splat(2*border1)));
|
|
draw.setColor(0xff000000);
|
|
draw.rect(pos - @as(Vec2f, @splat(border2)), size + @as(Vec2f, @splat(2*border2)));
|
|
textBuffer.render(pos[0], pos[1], 16);
|
|
}
|
|
};
|
|
}
|
|
}; |