From 178489b044df2dd632e1fc50fed79de0341ca1dc Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Fri, 26 Jan 2024 00:14:51 +0100 Subject: [PATCH] Merge All ItemSlot types into one struct. Fixes #240 --- src/gui/components/CraftingResultSlot.zig | 111 ---------------------- src/gui/components/ImmutableItemSlot.zig | 73 -------------- src/gui/components/ItemSlot.zig | 69 ++++++++++---- src/gui/gui.zig | 29 ++---- src/gui/gui_component.zig | 4 - src/gui/windows/creative_inventory.zig | 20 +--- src/gui/windows/hotbar.zig | 14 ++- src/gui/windows/inventory.zig | 14 ++- src/gui/windows/inventory_crafting.zig | 14 +-- src/gui/windows/workbench.zig | 20 ++-- src/items.zig | 21 ++-- src/renderer.zig | 6 +- 12 files changed, 108 insertions(+), 287 deletions(-) delete mode 100644 src/gui/components/CraftingResultSlot.zig delete mode 100644 src/gui/components/ImmutableItemSlot.zig diff --git a/src/gui/components/CraftingResultSlot.zig b/src/gui/components/CraftingResultSlot.zig deleted file mode 100644 index d78e196f..00000000 --- a/src/gui/components/CraftingResultSlot.zig +++ /dev/null @@ -1,111 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; - -const main = @import("root"); -const ItemStack = main.items.ItemStack; -const graphics = main.graphics; -const draw = graphics.draw; -const Texture = graphics.Texture; -const TextBuffer = graphics.TextBuffer; -const vec = main.vec; -const Vec2f = vec.Vec2f; - -const gui = @import("../gui.zig"); -const GuiComponent = gui.GuiComponent; - -const CraftingResultSlot = @This(); - -var texture: Texture = undefined; -const border: f32 = 3; - -pos: Vec2f, -size: Vec2f = .{24 + 2*border, 24 + 2*border}, -itemStack: ItemStack, -text: TextBuffer, -textSize: Vec2f = .{0, 0}, -onTake: gui.Callback, -hovered: bool = false, -pressed: bool = false, - -pub fn __init() !void { - texture = try Texture.initFromFile("assets/cubyz/ui/inventory/crafting_result_slot.png"); -} - -pub fn __deinit() void { - texture.deinit(); -} - -pub fn init(pos: Vec2f, itemStack: ItemStack, onTake: gui.Callback) Allocator.Error!*CraftingResultSlot { - const self = try main.globalAllocator.create(CraftingResultSlot); - var buf: [16]u8 = undefined; - self.* = CraftingResultSlot { - .itemStack = itemStack, - .pos = pos, - .text = try TextBuffer.init(main.globalAllocator, std.fmt.bufPrint(&buf, "{}", .{self.itemStack.amount}) catch "∞", .{}, false, .right), - .onTake = onTake, - }; - self.textSize = try self.text.calculateLineBreaks(8, self.size[0] - 2*border); - return self; -} - -pub fn deinit(self: *const CraftingResultSlot) void { - self.text.deinit(); - main.globalAllocator.destroy(self); -} - -pub fn toComponent(self: *CraftingResultSlot) GuiComponent { - return GuiComponent{ - .craftingResultSlot = self - }; -} - -pub fn updateHovered(self: *CraftingResultSlot, _: Vec2f) void { - self.hovered = true; - gui.hoveredCraftingSlot = self; -} - -pub fn mainButtonPressed(self: *CraftingResultSlot, _: Vec2f) void { - self.pressed = true; -} - -pub fn mainButtonReleased(self: *CraftingResultSlot, mousePosition: Vec2f) void { - if(self.pressed) { - self.pressed = false; - if(GuiComponent.contains(self.pos, self.size, mousePosition)) { - if(self.itemStack.item == null) return; - if(gui.inventory.carriedItemStack.item == null or std.meta.eql(self.itemStack.item, gui.inventory.carriedItemStack.item)) { - if(std.math.add(u16, gui.inventory.carriedItemStack.amount, self.itemStack.amount) catch null) |nextAmount| if(nextAmount <= self.itemStack.item.?.stackSize()) { - const itemStack = self.itemStack; - self.onTake.run(); - gui.inventory.carriedItemStack.item = itemStack.item; - gui.inventory.carriedItemStack.amount += itemStack.amount; - }; - } - } - } -} - -pub fn render(self: *CraftingResultSlot, _: Vec2f) !void { - draw.setColor(0xffffffff); - texture.bindTo(0); - draw.boundImage(self.pos, self.size); - if(self.itemStack.item) |item| { - const itemTexture = try item.getTexture(); - itemTexture.bindTo(0); - draw.setColor(0xff000000); - draw.boundImage(self.pos + @as(Vec2f, @splat(border)) + Vec2f{1.0, 1.0}, self.size - @as(Vec2f, @splat(2*border))); - draw.setColor(0xffffffff); - draw.boundImage(self.pos + @as(Vec2f, @splat(border)), self.size - @as(Vec2f, @splat(2*border))); - if(self.itemStack.amount != 1) { - try self.text.render(self.pos[0] + self.size[0] - self.textSize[0] - border, self.pos[1] + self.size[1] - self.textSize[1] - border, 8); - } - } - if(self.pressed) { - draw.setColor(0x80808080); - draw.rect(self.pos, self.size); - } else if(self.hovered) { - self.hovered = false; - draw.setColor(0x300000ff); - draw.rect(self.pos, self.size); - } -} \ No newline at end of file diff --git a/src/gui/components/ImmutableItemSlot.zig b/src/gui/components/ImmutableItemSlot.zig deleted file mode 100644 index 4df97c23..00000000 --- a/src/gui/components/ImmutableItemSlot.zig +++ /dev/null @@ -1,73 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; - -const main = @import("root"); -const BaseItem = main.items.BaseItem; -const graphics = main.graphics; -const draw = graphics.draw; -const Texture = graphics.Texture; -const TextBuffer = graphics.TextBuffer; -const vec = main.vec; -const Vec2f = vec.Vec2f; - -const gui = @import("../gui.zig"); -const GuiComponent = gui.GuiComponent; - -const ImmutableItemSlot = @This(); - -var texture: Texture = undefined; -const border: f32 = 3; - -pos: Vec2f, -size: Vec2f = .{24 + 2*border, 24 + 2*border}, -item: *BaseItem, -amount: u32, -text: TextBuffer, -textSize: Vec2f = .{0, 0}, - -pub fn __init() !void { - texture = try Texture.initFromFile("assets/cubyz/ui/inventory/immutable_slot.png"); -} - -pub fn __deinit() void { - texture.deinit(); -} - -pub fn init(pos: Vec2f, item: *BaseItem, amount: u32) Allocator.Error!*ImmutableItemSlot { - const self = try main.globalAllocator.create(ImmutableItemSlot); - var buf: [16]u8 = undefined; - self.* = ImmutableItemSlot { - .item = item, - .amount = amount, - .pos = pos, - .text = try TextBuffer.init(main.globalAllocator, std.fmt.bufPrint(&buf, "{}", .{amount}) catch "∞", .{}, false, .right), - }; - self.textSize = try self.text.calculateLineBreaks(8, self.size[0] - 2*border); - return self; -} - -pub fn deinit(self: *const ImmutableItemSlot) void { - self.text.deinit(); - main.globalAllocator.destroy(self); -} - -pub fn toComponent(self: *ImmutableItemSlot) GuiComponent { - return GuiComponent{ - .immutableItemSlot = self - }; -} - -pub fn render(self: *ImmutableItemSlot, _: Vec2f) !void { - draw.setColor(0xffffffff); - texture.bindTo(0); - draw.boundImage(self.pos, self.size); - const itemTexture = try self.item.getTexture(); - itemTexture.bindTo(0); - draw.setColor(0xff000000); - draw.boundImage(self.pos + @as(Vec2f, @splat(border)) + Vec2f{1.0, 1.0}, self.size - @as(Vec2f, @splat(2*border))); - draw.setColor(0xffffffff); - draw.boundImage(self.pos + @as(Vec2f, @splat(border)), self.size - @as(Vec2f, @splat(2*border))); - if(self.amount != 1) { - try self.text.render(self.pos[0] + self.size[0] - self.textSize[0] - border, self.pos[1] + self.size[1] - self.textSize[1] - border, 8); - } -} \ No newline at end of file diff --git a/src/gui/components/ItemSlot.zig b/src/gui/components/ItemSlot.zig index aa87cb8c..66f0af8c 100644 --- a/src/gui/components/ItemSlot.zig +++ b/src/gui/components/ItemSlot.zig @@ -16,13 +16,22 @@ const GuiComponent = gui.GuiComponent; const ItemSlot = @This(); -var texture: Texture = undefined; const border: f32 = 2; pub const VTable = struct { - tryAddingItems: *const fn(usize, *ItemStack, u16) void, - tryTakingItems: *const fn(usize, *ItemStack, u16) void, - trySwappingItems: *const fn(usize, *ItemStack) void, + tryAddingItems: *const fn(usize, *ItemStack, u16) void = &defaultAddingItems, + tryTakingItems: *const fn(usize, *ItemStack, u16) void = &defaultTakingItems, + trySwappingItems: *const fn(usize, *ItemStack) void = &defaultSwappingItems, + + fn defaultAddingItems(_: usize, _: *ItemStack, _: u16) void {} + fn defaultTakingItems(_: usize, _: *ItemStack, _: u16) void {} + fn defaultSwappingItems(_: usize, _: *ItemStack) void {} +}; + +const Mode = enum { + normal, + takeOnly, + immutable, }; pos: Vec2f, @@ -35,16 +44,40 @@ pressed: bool = false, renderFrame: bool = true, userData: usize, vtable: *const VTable, +texture: Texture, +mode: Mode, + +var defaultTexture: Texture = undefined; +var immutableTexture: Texture = undefined; +var craftingResultTexture: Texture = undefined; +const TextureParamType = union(enum) { + default: void, + immutable: void, + craftingResult: void, + custom: Texture, + fn value(self: TextureParamType) Texture { + return switch(self) { + .default => defaultTexture, + .immutable => immutableTexture, + .craftingResult => craftingResultTexture, + .custom => |t| t, + }; + } +}; pub fn __init() !void { - texture = try Texture.initFromFile("assets/cubyz/ui/inventory/slot.png"); + defaultTexture = try Texture.initFromFile("assets/cubyz/ui/inventory/slot.png"); + immutableTexture = try Texture.initFromFile("assets/cubyz/ui/inventory/immutable_slot.png"); + craftingResultTexture = try Texture.initFromFile("assets/cubyz/ui/inventory/crafting_result_slot.png"); } pub fn __deinit() void { - texture.deinit(); + defaultTexture.deinit(); + immutableTexture.deinit(); + craftingResultTexture.deinit(); } -pub fn init(pos: Vec2f, itemStack: ItemStack, vtable: *const VTable, userData: usize) Allocator.Error!*ItemSlot { +pub fn init(pos: Vec2f, itemStack: ItemStack, vtable: *const VTable, userData: usize, texture: TextureParamType, mode: Mode) Allocator.Error!*ItemSlot { const self = try main.globalAllocator.create(ItemSlot); var buf: [16]u8 = undefined; self.* = ItemSlot { @@ -53,6 +86,8 @@ pub fn init(pos: Vec2f, itemStack: ItemStack, vtable: *const VTable, userData: u .userData = userData, .pos = pos, .text = try TextBuffer.init(main.globalAllocator, std.fmt.bufPrint(&buf, "{}", .{self.itemStack.amount}) catch "∞", .{}, false, .right), + .texture = texture.value(), + .mode = mode, }; self.textSize = try self.text.calculateLineBreaks(8, self.size[0] - 2*border); return self; @@ -63,14 +98,14 @@ pub fn deinit(self: *const ItemSlot) void { main.globalAllocator.destroy(self); } -pub fn tryAddingItems(self: *ItemSlot, source: *ItemStack, amount: u16) void { +pub fn tryAddingItems(self: *ItemSlot, source: *ItemStack, desiredAmount: u16) void { std.debug.assert(source.item != null); - std.debug.assert(amount <= source.amount); - self.vtable.tryAddingItems(self.userData, source, amount); + std.debug.assert(desiredAmount <= source.amount); + self.vtable.tryAddingItems(self.userData, source, desiredAmount); } -pub fn tryTakingItems(self: *ItemSlot, destination: *ItemStack, amount: u16) void { - self.vtable.tryTakingItems(self.userData, destination, amount); +pub fn tryTakingItems(self: *ItemSlot, destination: *ItemStack, desiredAmount: u16) void { + self.vtable.tryTakingItems(self.userData, destination, desiredAmount); } pub fn trySwappingItems(self: *ItemSlot, destination: *ItemStack) void { @@ -122,7 +157,7 @@ pub fn mainButtonReleased(self: *ItemSlot, _: Vec2f) void { pub fn render(self: *ItemSlot, _: Vec2f) !void { draw.setColor(0xffffffff); if(self.renderFrame) { - texture.bindTo(0); + self.texture.bindTo(0); draw.boundImage(self.pos, self.size); } if(self.itemStack.item) |item| { @@ -140,8 +175,10 @@ pub fn render(self: *ItemSlot, _: Vec2f) !void { draw.setColor(0x80808080); draw.rect(self.pos, self.size); } else if(self.hovered) { - self.hovered = false; - draw.setColor(0x300000ff); - draw.rect(self.pos, self.size); + if(self.mode != .immutable) { + self.hovered = false; + draw.setColor(0x300000ff); + draw.rect(self.pos, self.size); + } } } \ No newline at end of file diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 1a720ef6..72cc3a5f 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -11,8 +11,6 @@ const Vec2f = vec.Vec2f; const Button = @import("components/Button.zig"); const CheckBox = @import("components/CheckBox.zig"); -const CraftingResultSlot = @import("components/CraftingResultSlot.zig"); -const ImmutableItemSlot = @import("components/ImmutableItemSlot.zig"); const ItemSlot = @import("components/ItemSlot.zig"); const ScrollBar = @import("components/ScrollBar.zig"); const ContinuousSlider = @import("components/ContinuousSlider.zig"); @@ -33,7 +31,6 @@ var hoveredAWindow: bool = false; pub var scale: f32 = undefined; pub var hoveredItemSlot: ?*ItemSlot = null; -pub var hoveredCraftingSlot: ?*CraftingResultSlot = null; const GuiCommandQueue = struct { const Action = enum { @@ -147,8 +144,6 @@ pub fn init() !void { try GuiWindow.__init(); try Button.__init(); try CheckBox.__init(); - try CraftingResultSlot.__init(); - try ImmutableItemSlot.__init(); try ItemSlot.__init(); try ScrollBar.__init(); try ContinuousSlider.__init(); @@ -171,8 +166,6 @@ pub fn deinit() void { GuiWindow.__deinit(); Button.__deinit(); CheckBox.__deinit(); - CraftingResultSlot.__deinit(); - ImmutableItemSlot.__deinit(); ItemSlot.__deinit(); ScrollBar.__deinit(); ContinuousSlider.__deinit(); @@ -444,12 +437,6 @@ pub fn mainButtonPressed() void { inventory.update() catch |err| { std.log.err("Encountered error while updating inventory: {s}", .{@errorName(err)}); }; - if(inventory.carriedItemStack.amount != 0) { - if(hoveredCraftingSlot) |hovered| { - hovered.mainButtonPressed(undefined); - } - return; - } selectedWindow = null; selectedTextInput = null; var selectedI: usize = 0; @@ -527,7 +514,6 @@ pub fn updateAndRenderGui() !void { try selected.updateSelected(mousePos); } hoveredItemSlot = null; - hoveredCraftingSlot = null; var i: usize = openWindows.items.len; while(i != 0) { i -= 1; @@ -568,7 +554,7 @@ pub const inventory = struct { pub fn init() !void { deliveredItemSlots = std.ArrayList(*ItemSlot).init(main.globalAllocator); deliveredItemStacksAmountAdded = std.ArrayList(u16).init(main.globalAllocator); - carriedItemSlot = try ItemSlot.init(.{0, 0}, carriedItemStack, undefined, undefined); + carriedItemSlot = try ItemSlot.init(.{0, 0}, carriedItemStack, undefined, undefined, .default, .normal); carriedItemSlot.renderFrame = false; } @@ -584,6 +570,7 @@ pub const inventory = struct { 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; @@ -619,7 +606,6 @@ pub const inventory = struct { } } } - try carriedItemSlot.updateItemStack(carriedItemStack); } fn applyChanges(leftClick: bool) void { @@ -633,11 +619,15 @@ pub const inventory = struct { } else if(hoveredItemSlot) |hovered| { if(carriedItemStack.amount != 0) { if(leftClick) { - hovered.trySwappingItems(&carriedItemStack); + 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, std.math.maxInt(u16)); + hovered.tryTakingItems(&carriedItemStack, hovered.itemStack.amount); } else { hovered.tryTakingItems(&carriedItemStack, hovered.itemStack.amount/2); } @@ -652,12 +642,13 @@ pub const inventory = struct { main.network.Protocols.genericUpdate.itemStackDrop(main.game.world.?.conn, .{.item = carriedItemStack.item, .amount = 1}, @floatCast(main.game.Player.getPosBlocking()), main.game.camera.direction, 20) catch |err| { std.log.err("Error while dropping itemStack: {s}", .{@errorName(err)}); }; - _ = carriedItemStack.add(@as(i32, -1)); + _ = carriedItemStack.add(carriedItemStack.item.?, @as(i32, -1)); } } } fn render(mousePos: Vec2f) !void { + try carriedItemSlot.updateItemStack(carriedItemStack); carriedItemSlot.pos = mousePos - Vec2f{12, 12}; try carriedItemSlot.render(.{0, 0}); // Draw tooltip: diff --git a/src/gui/gui_component.zig b/src/gui/gui_component.zig index 3becc660..a7f8b365 100644 --- a/src/gui/gui_component.zig +++ b/src/gui/gui_component.zig @@ -8,10 +8,8 @@ pub const GuiComponent = union(enum) { pub const Button = @import("components/Button.zig"); pub const CheckBox = @import("components/CheckBox.zig"); - pub const CraftingResultSlot = @import("components/CraftingResultSlot.zig"); pub const HorizontalList = @import("components/HorizontalList.zig"); pub const Icon = @import("components/Icon.zig"); - pub const ImmutableItemSlot = @import("components/ImmutableItemSlot.zig"); pub const ItemSlot = @import("components/ItemSlot.zig"); pub const Label = @import("components/Label.zig"); pub const MutexComponent = @import("components/MutexComponent.zig"); @@ -24,10 +22,8 @@ pub const GuiComponent = union(enum) { button: *Button, checkBox: *CheckBox, - craftingResultSlot: *CraftingResultSlot, horizontalList: *HorizontalList, icon: *Icon, - immutableItemSlot: *ImmutableItemSlot, itemSlot: *ItemSlot, label: *Label, mutexComponent: *MutexComponent, diff --git a/src/gui/windows/creative_inventory.zig b/src/gui/windows/creative_inventory.zig index a3b43813..0b0e92d7 100644 --- a/src/gui/windows/creative_inventory.zig +++ b/src/gui/windows/creative_inventory.zig @@ -26,26 +26,12 @@ pub var window = GuiWindow { const padding: f32 = 8; var items: std.ArrayList(Item) = undefined; -pub fn tryAddingItems(_: usize, _: *ItemStack, _: u16) void { - return; -} - -pub fn tryTakingItems(index: usize, destination: *ItemStack, amount: u16) void { +pub fn tryTakingItems(index: usize, destination: *ItemStack, _: u16) void { if(destination.item != null and !std.meta.eql(destination.item.?, items.items[index])) return; destination.item = items.items[index]; - _ = destination.add(amount); + destination.amount = destination.item.?.stackSize(); } -pub fn trySwappingItems(_: usize, _: *ItemStack) void { - return; -} - -const vtable = ItemSlot.VTable { - .tryAddingItems = &tryAddingItems, - .tryTakingItems = &tryTakingItems, - .trySwappingItems = &trySwappingItems, -}; - pub fn onOpen() Allocator.Error!void { items = std.ArrayList(Item).init(main.globalAllocator); var itemIterator = main.items.iterator(); @@ -60,7 +46,7 @@ pub fn onOpen() Allocator.Error!void { for(0..8) |_| { if(i >= items.items.len) break; const item = items.items[i]; - try row.add(try ItemSlot.init(.{0, 0}, .{.item = item, .amount = item.stackSize()}, &vtable, i)); + try row.add(try ItemSlot.init(.{0, 0}, .{.item = item, .amount = item.stackSize()}, &.{.tryTakingItems = &tryTakingItems}, i, .default, .takeOnly)); i += 1; } try list.add(row); diff --git a/src/gui/windows/hotbar.zig b/src/gui/windows/hotbar.zig index 2c5ef6d0..1a0b31b2 100644 --- a/src/gui/windows/hotbar.zig +++ b/src/gui/windows/hotbar.zig @@ -29,13 +29,12 @@ pub var window = GuiWindow { var itemSlots: [8]*ItemSlot = undefined; -pub fn tryAddingItems(index: usize, source: *ItemStack, amount: u16) void { +pub fn tryAddingItems(index: usize, source: *ItemStack, desiredAmount: u16) void { Player.mutex.lock(); defer Player.mutex.unlock(); const destination = &Player.inventory__SEND_CHANGES_TO_SERVER.items[index]; if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, desiredAmount); source.amount -= actual; if(source.amount == 0) source.item = null; main.network.Protocols.genericUpdate.sendInventory_full(main.game.world.?.conn, Player.inventory__SEND_CHANGES_TO_SERVER) catch |err| { // TODO(post-java): Add better options to the protocol. @@ -43,16 +42,15 @@ pub fn tryAddingItems(index: usize, source: *ItemStack, amount: u16) void { }; } -pub fn tryTakingItems(index: usize, destination: *ItemStack, _amount: u16) void { - var amount = _amount; +pub fn tryTakingItems(index: usize, destination: *ItemStack, desiredAmount: u16) void { + var amount = desiredAmount; Player.mutex.lock(); defer Player.mutex.unlock(); const source = &Player.inventory__SEND_CHANGES_TO_SERVER.items[index]; if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; if(source.item == null) return; amount = @min(amount, source.amount); - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, amount); source.amount -= actual; if(source.amount == 0) source.item = null; main.network.Protocols.genericUpdate.sendInventory_full(main.game.world.?.conn, Player.inventory__SEND_CHANGES_TO_SERVER) catch |err| { // TODO(post-java): Add better options to the protocol. @@ -81,7 +79,7 @@ const vtable = ItemSlot.VTable { pub fn onOpen() Allocator.Error!void { const list = try HorizontalList.init(); for(0..8) |i| { - itemSlots[i] = try ItemSlot.init(.{0, 0}, Player.inventory__SEND_CHANGES_TO_SERVER.items[i], &vtable, i); + itemSlots[i] = try ItemSlot.init(.{0, 0}, Player.inventory__SEND_CHANGES_TO_SERVER.items[i], &vtable, i, .default, .normal); try list.add(itemSlots[i]); } list.finish(.{0, 0}, .center); diff --git a/src/gui/windows/inventory.zig b/src/gui/windows/inventory.zig index 1b8e5a88..c1f6dd59 100644 --- a/src/gui/windows/inventory.zig +++ b/src/gui/windows/inventory.zig @@ -40,13 +40,12 @@ pub fn deinit() void { var itemSlots: [24]*ItemSlot = undefined; -pub fn tryAddingItems(index: usize, source: *ItemStack, amount: u16) void { +pub fn tryAddingItems(index: usize, source: *ItemStack, desiredAmount: u16) void { Player.mutex.lock(); defer Player.mutex.unlock(); const destination = &Player.inventory__SEND_CHANGES_TO_SERVER.items[index]; if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, desiredAmount); source.amount -= actual; if(source.amount == 0) source.item = null; main.network.Protocols.genericUpdate.sendInventory_full(main.game.world.?.conn, Player.inventory__SEND_CHANGES_TO_SERVER) catch |err| { // TODO(post-java): Add better options to the protocol. @@ -54,16 +53,15 @@ pub fn tryAddingItems(index: usize, source: *ItemStack, amount: u16) void { }; } -pub fn tryTakingItems(index: usize, destination: *ItemStack, _amount: u16) void { - var amount = _amount; +pub fn tryTakingItems(index: usize, destination: *ItemStack, desiredAmount: u16) void { + var amount = desiredAmount; Player.mutex.lock(); defer Player.mutex.unlock(); const source = &Player.inventory__SEND_CHANGES_TO_SERVER.items[index]; if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; if(source.item == null) return; amount = @min(amount, source.amount); - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, amount); source.amount -= actual; if(source.amount == 0) source.item = null; main.network.Protocols.genericUpdate.sendInventory_full(main.game.world.?.conn, Player.inventory__SEND_CHANGES_TO_SERVER) catch |err| { // TODO(post-java): Add better options to the protocol. @@ -103,7 +101,7 @@ pub fn onOpen() Allocator.Error!void { const row = try HorizontalList.init(); for(0..8) |x| { const index: usize = y*8 + x; - const slot = try ItemSlot.init(.{0, 0}, Player.inventory__SEND_CHANGES_TO_SERVER.items[index], &vtable, index); + const slot = try ItemSlot.init(.{0, 0}, Player.inventory__SEND_CHANGES_TO_SERVER.items[index], &vtable, index, .default, .normal); itemSlots[index - 8] = slot; try row.add(slot); } diff --git a/src/gui/windows/inventory_crafting.zig b/src/gui/windows/inventory_crafting.zig index aa9315e1..91c30e04 100644 --- a/src/gui/windows/inventory_crafting.zig +++ b/src/gui/windows/inventory_crafting.zig @@ -16,8 +16,7 @@ const Button = GuiComponent.Button; const HorizontalList = GuiComponent.HorizontalList; const VerticalList = GuiComponent.VerticalList; const Icon = GuiComponent.Icon; -const CraftingResultSlot = GuiComponent.CraftingResultSlot; -const ImmutableItemSlot = GuiComponent.ImmutableItemSlot; +const ItemSlot = GuiComponent.ItemSlot; const inventory = @import("inventory.zig"); @@ -62,10 +61,12 @@ fn addItemStackToAvailable(itemStack: ItemStack) Allocator.Error!void { } } -fn onTake(recipeIndex: usize) void { +fn tryTakingItems(recipeIndex: usize, destination: *ItemStack, _: u16) void { const recipe = items.recipes()[recipeIndex]; + const resultItem = recipe.resultItem; + if(!destination.canAddAll(resultItem.item.?, resultItem.amount)) return; for(recipe.sourceItems, recipe.sourceAmounts) |item, _amount| { - var amount: u32 = _amount; + var amount = _amount; for(main.game.Player.inventory__SEND_CHANGES_TO_SERVER.items) |*itemStack| { if(itemStack.item) |invItem| { if(invItem == .baseItem and invItem.baseItem == item) { @@ -84,6 +85,7 @@ fn onTake(recipeIndex: usize) void { std.log.warn("Congratulations, you just managed to cheat {}*{s}, thanks to my lazy coding. Have fun with that :D", .{amount, item.id}); } } + std.debug.assert(destination.add(resultItem.item.?, resultItem.amount) == resultItem.amount); } fn findAvailableRecipes(list: *VerticalList) Allocator.Error!bool { @@ -127,14 +129,14 @@ fn findAvailableRecipes(list: *VerticalList) Allocator.Error!bool { if(col < remainder) itemsThisColumn += 1; const columnList = try VerticalList.init(.{0, 0}, std.math.inf(f32), 0); for(0..itemsThisColumn) |_| { - try columnList.add(try ImmutableItemSlot.init(.{0, 0}, recipe.sourceItems[i], recipe.sourceAmounts[i])); + try columnList.add(try ItemSlot.init(.{0, 0}, .{.item = .{.baseItem = recipe.sourceItems[i]}, .amount = recipe.sourceAmounts[i]}, &.{}, 0, .immutable, .immutable)); i += 1; } columnList.finish(.center); try rowList.add(columnList); } try rowList.add(try Icon.init(.{8, 0}, .{32, 32}, arrowTexture, false)); - const itemSlot = try CraftingResultSlot.init(.{8, 0}, recipe.resultItem, .{.callback = &onTake, .arg = recipeIndex}); + const itemSlot = try ItemSlot.init(.{8, 0}, recipe.resultItem, &.{.tryTakingItems = &tryTakingItems}, recipeIndex, .craftingResult, .takeOnly); try rowList.add(itemSlot); rowList.finish(.{0, 0}, .center); try list.add(rowList); diff --git a/src/gui/windows/workbench.zig b/src/gui/windows/workbench.zig index cd4ceb44..037b4412 100644 --- a/src/gui/windows/workbench.zig +++ b/src/gui/windows/workbench.zig @@ -19,8 +19,6 @@ const HorizontalList = GuiComponent.HorizontalList; const VerticalList = GuiComponent.VerticalList; const Icon = GuiComponent.Icon; const ItemSlot = GuiComponent.ItemSlot; -const CraftingResultSlot = GuiComponent.CraftingResultSlot; -const ImmutableItemSlot = GuiComponent.ImmutableItemSlot; const inventory = @import("inventory.zig"); const inventory_crafting = @import("inventory_crafting.zig"); @@ -40,7 +38,7 @@ var availableItems: [25]?*const BaseItem = undefined; var craftingGrid: [25]ItemStack = undefined; -var craftingResult: *CraftingResultSlot = undefined; +var craftingResult: *ItemSlot = undefined; var seed: u32 = undefined; @@ -52,8 +50,7 @@ pub fn tryAddingItems(index: usize, source: *ItemStack, amount: u16) void { if(source.item.?.baseItem.material == null) return; const destination = &craftingGrid[index]; if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, amount); source.amount -= actual; if(source.amount == 0) source.item = null; } @@ -64,8 +61,7 @@ pub fn tryTakingItems(index: usize, destination: *ItemStack, _amount: u16) void if(destination.item != null and !std.meta.eql(source.item, destination.item)) return; if(source.item == null) return; amount = @min(amount, source.amount); - destination.item = source.item; - const actual = destination.add(amount); + const actual = destination.add(source.item.?, amount); source.amount -= actual; if(source.amount == 0) source.item = null; } @@ -83,10 +79,12 @@ const vtable = ItemSlot.VTable { .trySwappingItems = &trySwappingItems, }; -fn onTake(_: usize) void { +fn onTake(_: usize, destination: *ItemStack, _: u16) void { + if(destination.item != null) return; + destination.* = craftingResult.itemStack; for(&craftingGrid) |*itemStack| { if(itemStack.item != null and itemStack.item.? == .baseItem and itemStack.item.?.baseItem.material != null) { - _ = itemStack.add(@as(i32, -1)); + _ = itemStack.add(itemStack.item.?, @as(i32, -1)); } } craftingResult.itemStack = .{}; @@ -137,7 +135,7 @@ pub fn onOpen() Allocator.Error!void { const row = try HorizontalList.init(); for(0..5) |x| { const index = x + y*5; - const slot = try ItemSlot.init(.{0, 0}, craftingGrid[index], &vtable, index); + const slot = try ItemSlot.init(.{0, 0}, craftingGrid[index], &vtable, index, .default, .normal); itemSlots[index] = slot; try row.add(slot); } @@ -147,7 +145,7 @@ pub fn onOpen() Allocator.Error!void { try list.add(grid); } try list.add(try Icon.init(.{8, 0}, .{32, 32}, inventory_crafting.arrowTexture, false)); - craftingResult = try CraftingResultSlot.init(.{8, 0}, .{}, .{.callback = &onTake}); + craftingResult = try ItemSlot.init(.{8, 0}, .{}, &.{.tryTakingItems = &onTake}, 0, .craftingResult, .takeOnly); try list.add(craftingResult); list.finish(.{padding, padding + 16}, .center); window.rootComponent = list.toComponent(); diff --git a/src/items.zig b/src/items.zig index b6951984..2d6fc389 100644 --- a/src/items.zig +++ b/src/items.zig @@ -1154,8 +1154,8 @@ pub const ItemStack = struct { } /// Returns the number of items actually added/removed. - pub fn add(self: *ItemStack, number: anytype) @TypeOf(number) { - std.debug.assert(self.item != null); + pub fn add(self: *ItemStack, item: Item, number: anytype) @TypeOf(number) { + if(self.item == null) self.item = item; var newAmount = self.amount + number; var returnValue = number; if(newAmount < 0) { @@ -1173,9 +1173,9 @@ pub const ItemStack = struct { } /// whether the given number of items can be added to this stack. - pub fn canAddAll(self: *const ItemStack, number: u16) bool { - std.debug.assert(self.item); - return @as(u32, self.amount) + number <= self.item.?.stackSize(); + pub fn canAddAll(self: *const ItemStack, item: Item, number: u16) bool { + if(self.item != null and !std.meta.eql(self.item.?, item)) return false; + return @as(u32, self.amount) + number <= item.stackSize(); } pub fn clear(self: *ItemStack) void { @@ -1225,14 +1225,13 @@ pub const Inventory = struct { var amount = _amount; for(self.items) |*stack| { if(!stack.empty() and std.meta.eql(stack.item, item) and !stack.filled()) { - amount -= stack.add(amount); + amount -= stack.add(item, amount); if(amount == 0) return 0; } } for(self.items) |*stack| { if(stack.empty()) { - stack.item = item; - amount -= stack.add(amount); + amount -= stack.add(item, amount); if(amount == 0) return 0; } } @@ -1288,7 +1287,7 @@ pub const Inventory = struct { const Recipe = struct { sourceItems: []*BaseItem, - sourceAmounts: []u32, + sourceAmounts: []u16, resultItem: ItemStack, }; @@ -1337,7 +1336,7 @@ pub fn registerRecipes(file: []const u8) !void { } var items = std.ArrayList(*BaseItem).init(main.globalAllocator); defer items.deinit(); - var itemAmounts = std.ArrayList(u32).init(main.globalAllocator); + var itemAmounts = std.ArrayList(u16).init(main.globalAllocator); defer itemAmounts.deinit(); var string = std.ArrayList(u8).init(main.globalAllocator); defer string.deinit(); @@ -1369,7 +1368,7 @@ pub fn registerRecipes(file: []const u8) !void { const item = shortcuts.get(id) orelse getByID(id) orelse continue; const recipe = Recipe { .sourceItems = try arena.allocator().dupe(*BaseItem, items.items), - .sourceAmounts = try arena.allocator().dupe(u32, itemAmounts.items), + .sourceAmounts = try arena.allocator().dupe(u16, itemAmounts.items), .resultItem = ItemStack{.item = Item{.baseItem = item}, .amount = amount}, }; try recipeList.append(recipe); diff --git a/src/renderer.zig b/src/renderer.zig index af3e4794..52d73103 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -782,7 +782,7 @@ pub const MeshSelection = struct { if(rotationMode.generateData(main.game.world.?, selectedPos, relPos, lastDir, neighborDir, &block, false)) { // TODO: world.updateBlock(bi.x, bi.y, bi.z, block.data); (→ Sending it over the network) try mesh_storage.updateBlock(selectedPos[0], selectedPos[1], selectedPos[2], block); - _ = inventoryStack.add(@as(i32, -1)); + _ = inventoryStack.add(item, @as(i32, -1)); return; } } @@ -795,7 +795,7 @@ pub const MeshSelection = struct { if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, false)) { // TODO: world.updateBlock(bi.x, bi.y, bi.z, block.data); (→ Sending it over the network) try mesh_storage.updateBlock(neighborPos[0], neighborPos[1], neighborPos[2], block); - _ = inventoryStack.add(@as(i32, -1)); + _ = inventoryStack.add(item, @as(i32, -1)); return; } } else { @@ -815,7 +815,7 @@ pub const MeshSelection = struct { if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, true)) { // TODO: world.updateBlock(bi.x, bi.y, bi.z, block.data); (→ Sending it over the network) try mesh_storage.updateBlock(neighborPos[0], neighborPos[1], neighborPos[2], block); - _ = inventoryStack.add(@as(i32, -1)); + _ = inventoryStack.add(item, @as(i32, -1)); return; } }