mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
750 lines
21 KiB
Zig
750 lines
21 KiB
Zig
const std = @import("std");
|
|
|
|
const main = @import("main");
|
|
const graphics = main.graphics;
|
|
const draw = graphics.draw;
|
|
const ZonElement = main.ZonElement;
|
|
const settings = main.settings;
|
|
const vec = main.vec;
|
|
const Vec2f = vec.Vec2f;
|
|
const List = main.List;
|
|
|
|
const NeverFailingAllocator = main.heap.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");
|
|
const GamepadCursor = @import("gamepad_cursor.zig");
|
|
|
|
var windowList: List(*GuiWindow) = undefined;
|
|
var hudWindows: List(*GuiWindow) = undefined;
|
|
pub var openWindows: List(*GuiWindow) = undefined;
|
|
var selectedWindow: ?*GuiWindow = null;
|
|
pub var selectedTextInput: ?*TextInput = null;
|
|
var hoveredAWindow: bool = false;
|
|
pub var reorderWindows: bool = false;
|
|
pub var hideGui: bool = false;
|
|
|
|
pub var scale: f32 = undefined;
|
|
|
|
pub var hoveredItemSlot: ?*ItemSlot = null;
|
|
|
|
const GuiCommandQueue = struct { // MARK: GuiCommandQueue
|
|
const Action = enum {
|
|
open,
|
|
close,
|
|
};
|
|
const Command = struct {
|
|
window: *GuiWindow,
|
|
action: Action,
|
|
};
|
|
|
|
var commands: main.utils.ConcurrentQueue(Command) = undefined;
|
|
|
|
fn init() void {
|
|
commands = .init(main.globalAllocator, 16);
|
|
}
|
|
|
|
fn deinit() void {
|
|
commands.deinit();
|
|
}
|
|
|
|
fn scheduleCommand(command: Command) void {
|
|
commands.enqueue(command);
|
|
}
|
|
|
|
fn executeCommands() void {
|
|
while(commands.dequeue()) |command| {
|
|
switch(command.action) {
|
|
.open => {
|
|
executeOpenWindowCommand(command.window);
|
|
},
|
|
.close => {
|
|
executeCloseWindowCommand(command.window);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn executeOpenWindowCommand(window: *GuiWindow) void {
|
|
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 {
|
|
defer updateWindowPositions();
|
|
if(selectedWindow == window) {
|
|
selectedWindow = null;
|
|
}
|
|
for(openWindows.items, 0..) |_openWindow, i| {
|
|
if(_openWindow == window) {
|
|
_ = openWindows.swapRemove(i);
|
|
window.onCloseFn();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
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 initWindowList() void {
|
|
GuiCommandQueue.init();
|
|
windowList = .init(main.globalAllocator);
|
|
hudWindows = .init(main.globalAllocator);
|
|
openWindows = .init(main.globalAllocator);
|
|
inline for(@typeInfo(windowlist).@"struct".decls) |decl| {
|
|
const windowStruct = @field(windowlist, decl.name);
|
|
windowStruct.window.id = decl.name;
|
|
addWindow(&windowStruct.window);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn deinitWindowList() void {
|
|
windowList.deinit();
|
|
hudWindows.deinit();
|
|
openWindows.deinit();
|
|
GuiCommandQueue.deinit();
|
|
}
|
|
|
|
pub fn init() void { // MARK: init()
|
|
inline for(@typeInfo(windowlist).@"struct".decls) |decl| {
|
|
const windowStruct = @field(windowlist, decl.name);
|
|
if(@hasDecl(windowStruct, "init")) {
|
|
windowStruct.init();
|
|
}
|
|
}
|
|
GuiWindow.__init();
|
|
Button.__init();
|
|
CheckBox.__init();
|
|
ItemSlot.__init();
|
|
ScrollBar.__init();
|
|
ContinuousSlider.__init();
|
|
DiscreteSlider.__init();
|
|
TextInput.__init();
|
|
load();
|
|
GamepadCursor.init();
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
save();
|
|
GamepadCursor.deinit();
|
|
for(openWindows.items) |window| {
|
|
window.onCloseFn();
|
|
}
|
|
openWindows.clearRetainingCapacity();
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn save() void { // MARK: save()
|
|
const guiZon = ZonElement.initObject(main.stackAllocator);
|
|
defer guiZon.deinit(main.stackAllocator);
|
|
for(windowList.items) |window| {
|
|
const windowZon = ZonElement.initObject(main.stackAllocator);
|
|
for(window.relativePosition, 0..) |relPos, i| {
|
|
const relPosZon = ZonElement.initObject(main.stackAllocator);
|
|
switch(relPos) {
|
|
.ratio => |ratio| {
|
|
relPosZon.put("type", "ratio");
|
|
relPosZon.put("ratio", ratio);
|
|
},
|
|
.attachedToFrame => |attachedToFrame| {
|
|
relPosZon.put("type", "attachedToFrame");
|
|
relPosZon.put("selfAttachmentPoint", @intFromEnum(attachedToFrame.selfAttachmentPoint));
|
|
relPosZon.put("otherAttachmentPoint", @intFromEnum(attachedToFrame.otherAttachmentPoint));
|
|
},
|
|
.relativeToWindow => |relativeToWindow| {
|
|
relPosZon.put("type", "relativeToWindow");
|
|
relPosZon.put("reference", relativeToWindow.reference.id);
|
|
relPosZon.put("ratio", relativeToWindow.ratio);
|
|
},
|
|
.attachedToWindow => |attachedToWindow| {
|
|
relPosZon.put("type", "attachedToWindow");
|
|
relPosZon.put("reference", attachedToWindow.reference.id);
|
|
relPosZon.put("selfAttachmentPoint", @intFromEnum(attachedToWindow.selfAttachmentPoint));
|
|
relPosZon.put("otherAttachmentPoint", @intFromEnum(attachedToWindow.otherAttachmentPoint));
|
|
},
|
|
}
|
|
windowZon.put(([_][]const u8{"relPos0", "relPos1"})[i], relPosZon);
|
|
}
|
|
windowZon.put("scale", window.scale);
|
|
guiZon.put(window.id, windowZon);
|
|
}
|
|
|
|
main.files.cubyzDir().writeZon("gui_layout.zig.zon", guiZon) catch |err| {
|
|
std.log.err("Could not write gui_layout.zig.zon: {s}", .{@errorName(err)});
|
|
};
|
|
}
|
|
|
|
fn load() void {
|
|
const zon: ZonElement = main.files.cubyzDir().readToZon(main.stackAllocator, "gui_layout.zig.zon") catch |err| blk: {
|
|
if(err != error.FileNotFound) {
|
|
std.log.err("Could not read gui_layout.zig.zon: {s}", .{@errorName(err)});
|
|
}
|
|
break :blk .null;
|
|
};
|
|
defer zon.deinit(main.stackAllocator);
|
|
|
|
for(windowList.items) |window| {
|
|
const windowZon = zon.getChild(window.id);
|
|
if(windowZon == .null) continue;
|
|
for(&window.relativePosition, 0..) |*relPos, i| {
|
|
const relPosZon = windowZon.getChild(([_][]const u8{"relPos0", "relPos1"})[i]);
|
|
const typ = relPosZon.get([]const u8, "type", "ratio");
|
|
if(std.mem.eql(u8, typ, "ratio")) {
|
|
relPos.* = .{.ratio = relPosZon.get(f32, "ratio", 0.5)};
|
|
} else if(std.mem.eql(u8, typ, "attachedToFrame")) {
|
|
relPos.* = .{.attachedToFrame = .{
|
|
.selfAttachmentPoint = @enumFromInt(relPosZon.get(u8, "selfAttachmentPoint", 0)),
|
|
.otherAttachmentPoint = @enumFromInt(relPosZon.get(u8, "otherAttachmentPoint", 0)),
|
|
}};
|
|
} else if(std.mem.eql(u8, typ, "relativeToWindow")) {
|
|
const reference = getWindowById(relPosZon.get([]const u8, "reference", "")) orelse continue;
|
|
relPos.* = .{.relativeToWindow = .{
|
|
.reference = reference,
|
|
.ratio = relPosZon.get(f32, "ratio", 0.5),
|
|
}};
|
|
} else if(std.mem.eql(u8, typ, "attachedToWindow")) {
|
|
const reference = getWindowById(relPosZon.get([]const u8, "reference", "")) orelse continue;
|
|
relPos.* = .{.attachedToWindow = .{
|
|
.reference = reference,
|
|
.selfAttachmentPoint = @enumFromInt(relPosZon.get(u8, "selfAttachmentPoint", 0)),
|
|
.otherAttachmentPoint = @enumFromInt(relPosZon.get(u8, "otherAttachmentPoint", 0)),
|
|
}};
|
|
} else {
|
|
std.log.err("Unknown window attachment type: {s}", .{typ});
|
|
}
|
|
}
|
|
window.scale = windowZon.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.err("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.err("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);
|
|
window.onCloseFn();
|
|
selectedWindow = null;
|
|
return;
|
|
}
|
|
}
|
|
openWindows.append(window);
|
|
window.onOpenFn();
|
|
selectedWindow = null;
|
|
return;
|
|
}
|
|
}
|
|
std.log.err("Could not find window with id {s}.", .{id});
|
|
}
|
|
|
|
pub fn openHud() void {
|
|
inventory.init();
|
|
for(windowList.items) |window| {
|
|
if(window.isHud) {
|
|
openWindowFromRef(window);
|
|
}
|
|
}
|
|
reorderWindows = false;
|
|
}
|
|
|
|
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 closeWindowFromRef(window: *GuiWindow) void {
|
|
GuiCommandQueue.scheduleCommand(.{.action = .close, .window = window});
|
|
}
|
|
|
|
pub fn closeWindow(id: []const u8) void {
|
|
for(windowList.items) |window| {
|
|
if(std.mem.eql(u8, window.id, id)) {
|
|
closeWindowFromRef(window);
|
|
return;
|
|
}
|
|
}
|
|
std.log.err("Could not find window with id {s}.", .{id});
|
|
}
|
|
|
|
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.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.left(mods);
|
|
}
|
|
}
|
|
pub fn right(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.right(mods);
|
|
}
|
|
}
|
|
pub fn down(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.down(mods);
|
|
}
|
|
}
|
|
pub fn up(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.up(mods);
|
|
}
|
|
}
|
|
pub fn gotoStart(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.gotoStart(mods);
|
|
}
|
|
}
|
|
pub fn gotoEnd(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.gotoEnd(mods);
|
|
}
|
|
}
|
|
pub fn deleteLeft(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.deleteLeft(mods);
|
|
}
|
|
}
|
|
pub fn deleteRight(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.deleteRight(mods);
|
|
}
|
|
}
|
|
pub fn selectAll(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.selectAll(mods);
|
|
}
|
|
}
|
|
pub fn copy(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.copy(mods);
|
|
}
|
|
}
|
|
pub fn paste(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.paste(mods);
|
|
}
|
|
}
|
|
pub fn cut(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.cut(mods);
|
|
}
|
|
}
|
|
pub fn newline(mods: main.Window.Key.Modifiers) void {
|
|
if(selectedTextInput) |current| {
|
|
current.newline(mods);
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn mainButtonPressed() void {
|
|
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 and inventory.carried.getItem(0) == null) {
|
|
toggleGameMenu();
|
|
}
|
|
}
|
|
|
|
pub fn mainButtonReleased() void {
|
|
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 {
|
|
inventory.update();
|
|
}
|
|
|
|
pub fn secondaryButtonReleased() void {
|
|
inventory.applyChanges(false);
|
|
}
|
|
|
|
pub fn updateWindowPositions() void {
|
|
var wasChanged: bool = true;
|
|
while(wasChanged) {
|
|
wasChanged = false;
|
|
for(windowList.items) |window| {
|
|
const oldPos = window.pos;
|
|
window.updateWindowPosition();
|
|
const newPos = window.pos;
|
|
if(vec.lengthSquare(oldPos - newPos) >= 1e-3) {
|
|
wasChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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(!hideGui) {
|
|
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);
|
|
}
|
|
const oldScale = draw.setScale(scale);
|
|
defer draw.restoreScale(oldScale);
|
|
GamepadCursor.render();
|
|
}
|
|
|
|
pub fn toggleGameMenu() void {
|
|
main.Window.setMouseGrabbed(!main.Window.grabbed);
|
|
if(main.Window.grabbed) { // Take of the currently held item stack and close some windows
|
|
main.game.Player.inventory.depositOrDrop(inventory.carried);
|
|
hoveredItemSlot = null;
|
|
var i: usize = 0;
|
|
while(i < openWindows.items.len) {
|
|
const window = openWindows.items[i];
|
|
if(window.closeIfMouseIsGrabbed) {
|
|
_ = openWindows.swapRemove(i);
|
|
window.onCloseFn();
|
|
} else {
|
|
i += 1;
|
|
}
|
|
}
|
|
reorderWindows = false;
|
|
}
|
|
}
|
|
|
|
pub const inventory = struct { // MARK: inventory
|
|
const ItemStack = main.items.ItemStack;
|
|
const Inventory = main.items.Inventory;
|
|
pub var carried: Inventory = undefined;
|
|
var carriedItemSlot: *ItemSlot = undefined;
|
|
var leftClickSlots: List(*ItemSlot) = undefined;
|
|
var rightClickSlots: List(*ItemSlot) = undefined;
|
|
var initialized: bool = false;
|
|
const minCraftingCooldown = 20;
|
|
const maxCraftingCooldown = 400;
|
|
var nextCraftingAction: i64 = undefined;
|
|
var craftingCooldown: u63 = undefined;
|
|
var startedCrafting: bool = false;
|
|
|
|
pub fn init() void {
|
|
carried = Inventory.init(main.globalAllocator, 1, .normal, .{.hand = main.game.Player.id});
|
|
leftClickSlots = .init(main.globalAllocator);
|
|
rightClickSlots = .init(main.globalAllocator);
|
|
carriedItemSlot = ItemSlot.init(.{0, 0}, carried, 0, .default, .normal);
|
|
carriedItemSlot.renderFrame = false;
|
|
initialized = true;
|
|
startedCrafting = false;
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
initialized = false;
|
|
carried.deinit(main.globalAllocator);
|
|
carriedItemSlot.deinit();
|
|
leftClickSlots.deinit();
|
|
rightClickSlots.deinit();
|
|
}
|
|
|
|
pub fn deleteItemSlotReferences(slot: *const ItemSlot) void {
|
|
if(slot == hoveredItemSlot) {
|
|
hoveredItemSlot = null;
|
|
}
|
|
var i: usize = 0;
|
|
while(i < leftClickSlots.items.len) {
|
|
if(leftClickSlots.items[i] == slot) {
|
|
_ = leftClickSlots.swapRemove(i);
|
|
continue;
|
|
}
|
|
i += 1;
|
|
}
|
|
i = 0;
|
|
while(i < rightClickSlots.items.len) {
|
|
if(rightClickSlots.items[i] == slot) {
|
|
_ = rightClickSlots.swapRemove(i);
|
|
continue;
|
|
}
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
fn update() void {
|
|
if(!initialized) return;
|
|
if(hoveredItemSlot) |itemSlot| {
|
|
if(itemSlot.inventory.type == .crafting and itemSlot.mode == .takeOnly) {
|
|
if(main.KeyBoard.key("mainGuiButton").pressed) {
|
|
const time = std.time.milliTimestamp();
|
|
if(!startedCrafting) {
|
|
startedCrafting = true;
|
|
craftingCooldown = maxCraftingCooldown;
|
|
nextCraftingAction = time;
|
|
}
|
|
while(time -% nextCraftingAction >= 0) {
|
|
nextCraftingAction +%= craftingCooldown;
|
|
craftingCooldown -= (craftingCooldown - minCraftingCooldown)*craftingCooldown/1000;
|
|
itemSlot.inventory.depositOrSwap(itemSlot.itemSlot, carried);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if(itemSlot.mode != .normal) return;
|
|
|
|
if(carried.getAmount(0) == 0) return;
|
|
if(main.KeyBoard.key("mainGuiButton").pressed) {
|
|
for(leftClickSlots.items) |deliveredSlot| {
|
|
if(itemSlot == deliveredSlot) {
|
|
return;
|
|
}
|
|
}
|
|
if(itemSlot.inventory.getItem(itemSlot.itemSlot) == null) {
|
|
leftClickSlots.append(itemSlot);
|
|
}
|
|
} else if(main.KeyBoard.key("secondaryGuiButton").pressed) {
|
|
for(rightClickSlots.items) |deliveredSlot| {
|
|
if(itemSlot == deliveredSlot) {
|
|
return;
|
|
}
|
|
}
|
|
itemSlot.inventory.deposit(itemSlot.itemSlot, carried, 1);
|
|
rightClickSlots.append(itemSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn applyChanges(leftClick: bool) void {
|
|
if(!initialized) return;
|
|
if(main.game.world == null) return;
|
|
if(leftClick) {
|
|
if(startedCrafting) {
|
|
startedCrafting = false;
|
|
return;
|
|
}
|
|
if(leftClickSlots.items.len != 0) {
|
|
const targetInventories = main.stackAllocator.alloc(Inventory, leftClickSlots.items.len);
|
|
defer main.stackAllocator.free(targetInventories);
|
|
const targetSlots = main.stackAllocator.alloc(u32, leftClickSlots.items.len);
|
|
defer main.stackAllocator.free(targetSlots);
|
|
for(0..leftClickSlots.items.len) |i| {
|
|
targetInventories[i] = leftClickSlots.items[i].inventory;
|
|
targetSlots[i] = leftClickSlots.items[i].itemSlot;
|
|
}
|
|
carried.distribute(targetInventories, targetSlots);
|
|
leftClickSlots.clearRetainingCapacity();
|
|
} else if(hoveredItemSlot) |hovered| {
|
|
hovered.inventory.depositOrSwap(hovered.itemSlot, carried);
|
|
} else if(!hoveredAWindow) {
|
|
carried.dropStack(0);
|
|
}
|
|
} else {
|
|
if(rightClickSlots.items.len != 0) {
|
|
rightClickSlots.clearRetainingCapacity();
|
|
} else if(hoveredItemSlot) |hovered| {
|
|
hovered.inventory.takeHalf(hovered.itemSlot, carried);
|
|
} else if(!hoveredAWindow) {
|
|
carried.dropOne(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render(mousePos: Vec2f) void {
|
|
if(!initialized) return;
|
|
carriedItemSlot.pos = mousePos - Vec2f{12, 12};
|
|
carriedItemSlot.render(.{0, 0});
|
|
// Draw tooltip:
|
|
if(carried.getAmount(0) == 0) if(hoveredItemSlot) |hovered| {
|
|
if(hovered.inventory.getItem(hovered.itemSlot)) |item| {
|
|
const tooltip = item.getTooltip();
|
|
var textBuffer = graphics.TextBuffer.init(main.stackAllocator, tooltip, .{}, false, .left);
|
|
defer textBuffer.deinit();
|
|
var size = textBuffer.calculateLineBreaks(16, 300);
|
|
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);
|
|
}
|
|
};
|
|
}
|
|
};
|