From 642624c94d9be30077c5654fcbb7c6915225fc65 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Thu, 23 Mar 2023 13:08:29 +0100 Subject: [PATCH] Add the ItemSlot component. --- assets/cubyz/ui/inventory/slot.png | Bin 0 -> 696 bytes src/gui/components/ItemSlot.zig | 110 +++++++++++++++++++++++++++++ src/gui/gui.zig | 3 + src/gui/gui_component.zig | 2 + src/gui/windows/hotbar.zig | 27 ++++++- src/itemdrop.zig | 2 +- src/items.zig | 58 +++++++++++---- 7 files changed, 188 insertions(+), 14 deletions(-) create mode 100644 assets/cubyz/ui/inventory/slot.png create mode 100644 src/gui/components/ItemSlot.zig diff --git a/assets/cubyz/ui/inventory/slot.png b/assets/cubyz/ui/inventory/slot.png new file mode 100644 index 0000000000000000000000000000000000000000..f129f9b253eca82ca831153121c679a28f811253 GIT binary patch literal 696 zcmeAS@N?(olHy`uVBq!ia0vp^ZXnFT1|$ph9xlq%tsQ zOst(~>v7mY=4gEI(nX@|++7+eEBwTc=*w*7a$O;i)fy)2vu^%{C5oPfg(5NBe04fu zvN3Z?OAAC<4)P0xi>teeYksWvT`?uQxX#mei>@` zfy@5ag;qtMlV`3xSJC7<|FM)`KK{Lu&FSBF<}bFMY%2LcxHElbUc?dUBbR4SJ6Ad1 z;s{q!+mAUFR~B#!*%!qY{_HKjm)Mxz){~Mn&%#CLS#(5ZVwge5BhN<#ZdWzpJ$BA{ zxaVl3ZfL~gd4F0DuGzNfOzEuVjzmH42?|b*tgcfM6rEPPb#=15b+`S)+!wDj-D#bY zgr!(O8`sT`&HoR+&pUhP$d1_}Jxp))xBWHbVJT~@y1H$Df9~!&rXTYgwb#DXzuqv@ z|I%t--5QoJIn3hMeSL2->y#M7JcmajBdb&7*rU@{`;_^J56`;lGv>(t4;;T8F+4bXQkx1 zuGT*G%#FO=ng1l8mh9Ve@6@5*8h#!LgM`r}J|S~HA9gyios?)fd+_L*Jd3eW=#p00i_>zopr0RP7mkN^Mx literal 0 HcmV?d00001 diff --git a/src/gui/components/ItemSlot.zig b/src/gui/components/ItemSlot.zig new file mode 100644 index 00000000..73e93e80 --- /dev/null +++ b/src/gui/components/ItemSlot.zig @@ -0,0 +1,110 @@ +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 ItemSlot = @This(); + +var texture: Texture = undefined; +const border: f32 = 3; + +pos: Vec2f, +size: Vec2f = .{32 + 2*border, 32 + 2*border}, +itemStack: *ItemStack, +oldStack: ItemStack, +text: TextBuffer, +textSize: Vec2f = .{0, 0}, +hovered: bool = false, +pressed: bool = false, + +pub fn __init() !void { + texture = try Texture.initFromFile("assets/cubyz/ui/inventory/slot.png"); +} + +pub fn __deinit() void { + texture.deinit(); +} + +pub fn init(pos: Vec2f, itemStack: *ItemStack) Allocator.Error!*ItemSlot { + const self = try gui.allocator.create(ItemSlot); + var buf: [16]u8 = undefined; + self.* = ItemSlot { + .itemStack = itemStack, + .oldStack = itemStack.*, + .pos = pos, + .text = try TextBuffer.init(gui.allocator, std.fmt.bufPrint(&buf, "{}", .{self.itemStack.amount}) catch "∞", .{}, false, .right), + }; + self.textSize = try self.text.calculateLineBreaks(8, self.size[0] - 2*border); + return self; +} + +pub fn deinit(self: *const ItemSlot) void { + self.text.deinit(); + gui.allocator.destroy(self); +} + +fn refreshText(self: *ItemSlot) !void { + self.text.deinit(); + var buf: [16]u8 = undefined; + self.text = try TextBuffer.init(gui.allocator, std.fmt.bufPrint(&buf, "{}", .{self.itemStack.amount}) catch "∞", .{}, false, .right); + self.textSize = try self.text.calculateLineBreaks(8, self.size[0] - 2*border); +} + +pub fn toComponent(self: *ItemSlot) GuiComponent { + return GuiComponent{ + .itemSlot = self + }; +} + +pub fn updateHovered(self: *ItemSlot, _: Vec2f) void { + self.hovered = true; +} + +pub fn mainButtonPressed(self: *ItemSlot, _: Vec2f) void { + self.pressed = true; +} + +pub fn mainButtonReleased(self: *ItemSlot, mousePosition: Vec2f) void { + if(self.pressed) { + self.pressed = false; + if(GuiComponent.contains(self.pos, self.size, mousePosition)) { + //TODO: self.onAction(); + } + } +} + +pub fn render(self: *ItemSlot, _: Vec2f) !void { + const newStack = self.itemStack.*; + if(newStack.amount != self.oldStack.amount) { + try self.refreshText(); + } + 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.boundImage(self.pos + @splat(2, border), self.size - @splat(2, 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/gui.zig b/src/gui/gui.zig index f4b405ba..c21905dd 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -11,6 +11,7 @@ const Vec2f = vec.Vec2f; 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 Slider = @import("components/Slider.zig"); const TextInput = @import("components/TextInput.zig"); @@ -46,6 +47,7 @@ pub fn init(_allocator: Allocator) !void { try GuiWindow.__init(); try Button.__init(); try CheckBox.__init(); + try ItemSlot.__init(); try ScrollBar.__init(); try Slider.__init(); try TextInput.__init(); @@ -65,6 +67,7 @@ pub fn deinit() void { GuiWindow.__deinit(); Button.__deinit(); CheckBox.__deinit(); + ItemSlot.__deinit(); ScrollBar.__deinit(); Slider.__deinit(); TextInput.__deinit(); diff --git a/src/gui/gui_component.zig b/src/gui/gui_component.zig index 54bcadac..dd61410b 100644 --- a/src/gui/gui_component.zig +++ b/src/gui/gui_component.zig @@ -9,6 +9,7 @@ pub const GuiComponent = union(enum) { pub const Button = @import("components/Button.zig"); pub const CheckBox = @import("components/CheckBox.zig"); pub const HorizontalList = @import("components/HorizontalList.zig"); + pub const ItemSlot = @import("components/ItemSlot.zig"); pub const Label = @import("components/Label.zig"); pub const MutexComponent = @import("components/MutexComponent.zig"); pub const Slider = @import("components/Slider.zig"); @@ -20,6 +21,7 @@ pub const GuiComponent = union(enum) { button: *Button, checkBox: *CheckBox, horizontalList: *HorizontalList, + itemSlot: *ItemSlot, label: *Label, mutexComponent: *MutexComponent, scrollBar: *ScrollBar, diff --git a/src/gui/windows/hotbar.zig b/src/gui/windows/hotbar.zig index cd69a273..bad33537 100644 --- a/src/gui/windows/hotbar.zig +++ b/src/gui/windows/hotbar.zig @@ -2,20 +2,45 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const main = @import("root"); +const Player = main.game.Player; const Vec2f = main.vec.Vec2f; const gui = @import("../gui.zig"); const GuiComponent = gui.GuiComponent; const GuiWindow = gui.GuiWindow; +const HorizontalList = GuiComponent.HorizontalList; +const ItemSlot = GuiComponent.ItemSlot; +var components: [1]GuiComponent = undefined; pub var window = GuiWindow { .contentSize = Vec2f{64*8, 64}, .title = "Hotbar", .id = "cubyz:hotbar", + .onOpenFn = &onOpen, + .onCloseFn = &onClose, .renderFn = &render, + .components = &components, .isHud = true, + .showTitleBar = false, + .hasBackground = false, }; -pub fn render() Allocator.Error!void { +pub fn onOpen() Allocator.Error!void { + var list = try HorizontalList.init(); + for(0..8) |i| { + try list.add(try ItemSlot.init(.{0, 0}, &Player.inventory__SEND_CHANGES_TO_SERVER.items[i])); + } + list.finish(.{0, 0}, .center); + components[0] = list.toComponent(); + window.contentSize = components[0].size(); + gui.updateWindowPositions(); +} +pub fn onClose() void { + for(&components) |*comp| { + comp.deinit(); + } +} + +pub fn render() Allocator.Error!void { } \ No newline at end of file diff --git a/src/itemdrop.zig b/src/itemdrop.zig index c3dc23b8..bb535dba 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -570,7 +570,7 @@ pub const ItemDropRenderer = struct { .item = template.item, }; // Find sizes and free index: - const img = self.item.getTexture(); + const img = self.item.getImage(); self.size = Vec3i{img.width, 1, img.height}; var freeSlot: ?*ItemVoxelModel = null; for(freeSlots.items, 0..) |potentialSlot, i| { diff --git a/src/items.zig b/src/items.zig index f9e81e6e..26e88b6d 100644 --- a/src/items.zig +++ b/src/items.zig @@ -67,7 +67,8 @@ const Material = struct { pub const BaseItem = struct { - texture: graphics.Image, + image: graphics.Image, + texture: ?graphics.Texture, // TODO: Properly deinit id: []const u8, name: []const u8, @@ -78,7 +79,7 @@ pub const BaseItem = struct { fn init(self: *BaseItem, allocator: Allocator, texturePath: []const u8, replacementTexturePath: []const u8, id: []const u8, json: JsonElement) !void { self.id = try allocator.dupe(u8, id); - self.texture = graphics.Image.readFromFile(allocator, texturePath) catch graphics.Image.readFromFile(allocator, replacementTexturePath) catch blk: { + self.image = graphics.Image.readFromFile(allocator, texturePath) catch graphics.Image.readFromFile(allocator, replacementTexturePath) catch blk: { std.log.err("Item texture not found in {s} and {s}.", .{texturePath, replacementTexturePath}); break :blk graphics.Image.defaultImage; }; @@ -94,6 +95,7 @@ pub const BaseItem = struct { self.block = blk: { break :blk blocks.getByID(json.get(?[]const u8, "block", null) orelse break :blk null); }; + self.texture = null; self.foodValue = json.get(f32, "food", 0); } @@ -104,6 +106,14 @@ pub const BaseItem = struct { } return hash; } + + fn getTexture(self: *BaseItem) !graphics.Texture { + if(self.texture == null) { + self.texture = graphics.Texture.init(); + try self.texture.?.generate(self.image); + } + return self.texture.?; + } // TODO: Check if/how this is needed: // protected Item(int stackSize) { // id = Resource.EMPTY; @@ -412,7 +422,7 @@ const TextureGenerator = struct { } pub fn generate(tool: *Tool) !void { - const img = tool.texture; + const img = tool.image; var pixelMaterials: [16][16]PixelData = undefined; for(0..16) |x| { for(0..16) |y| { @@ -869,7 +879,8 @@ const ToolPhysics = struct { const Tool = struct { craftingGrid: [25]?*const BaseItem, materialGrid: [16][16]?*const BaseItem, - texture: graphics.Image, + image: graphics.Image, + texture: ?graphics.Texture, seed: u32, /// Reduction factor to block breaking time. @@ -901,12 +912,16 @@ const Tool = struct { pub fn init() !*Tool { var self = try main.globalAllocator.create(Tool); - self.texture = try graphics.Image.init(main.globalAllocator, 16, 16); + self.image = try graphics.Image.init(main.globalAllocator, 16, 16); + self.texture = null; return self; } pub fn deinit(self: *const Tool) void { - self.texture.deinit(main.globalAllocator); + if(self.texture) |texture| { + texture.deinit(); + } + self.image.deinit(main.globalAllocator); main.globalAllocator.destroy(self); } @@ -961,6 +976,14 @@ const Tool = struct { return hash; } + fn getTexture(self: *Tool) !graphics.Texture { + if(self.texture == null) { + self.texture = graphics.Texture.init(); + try self.texture.?.generate(self.image); + } + return self.texture.?; + } + pub fn getPowerByBlockClass(self: *Tool, blockClass: blocks.BlockClass) f32 { return switch(blockClass) { .fluid => 0, @@ -979,8 +1002,8 @@ const Tool = struct { }; pub const Item = union(enum) { - baseItem: *const BaseItem, - tool: *const Tool, + baseItem: *BaseItem, + tool: *Tool, pub fn init(json: JsonElement) !Item { if(reverseIndices.get(json.get([]const u8, "item", "null"))) |baseItem| { @@ -1026,13 +1049,24 @@ pub const Item = union(enum) { } } - pub fn getTexture(self: Item) graphics.Image { + pub fn getTexture(self: Item) !graphics.Texture { switch(self) { .baseItem => |_baseItem| { - return _baseItem.texture; + return try _baseItem.getTexture(); }, .tool => |_tool| { - return _tool.texture; + return try _tool.getTexture(); + }, + } + } + + pub fn getImage(self: Item) graphics.Image { + switch(self) { + .baseItem => |_baseItem| { + return _baseItem.image; + }, + .tool => |_tool| { + return _tool.image; }, } } @@ -1256,7 +1290,7 @@ pub fn deinit() void { arena.deinit(); } -pub fn getByID(id: []const u8) ?*const BaseItem { +pub fn getByID(id: []const u8) ?*BaseItem { if(reverseIndices.get(id)) |result| { return result; } else {