diff --git a/assets/cubyz/blocks/transmutation_workbench.zig.zon b/assets/cubyz/blocks/transmutation_workbench.zig.zon new file mode 100644 index 0000000000..032cfdfb4c --- /dev/null +++ b/assets/cubyz/blocks/transmutation_workbench.zig.zon @@ -0,0 +1,14 @@ +.{ + .tags = .{.diggable}, + .blockHealth = 6.5, + .drops = .{ + .{.items = .{.auto}}, + }, + .model = "cubyz:cube", + .rotation = "cubyz:stairs", + .texture = "cubyz:aerosoil", + .onInteract = .{ + .type = .open_crafting_window, + .craftingTags = .{.alchemy}, + }, +} diff --git a/assets/cubyz/recipes/cloth_recipes.zig.zon b/assets/cubyz/recipes/cloth_recipes.zig.zon index 9a5e00ffa3..a8da98d2d1 100644 --- a/assets/cubyz/recipes/cloth_recipes.zig.zon +++ b/assets/cubyz/recipes/cloth_recipes.zig.zon @@ -4,4 +4,10 @@ .output = "cubyz:cloth/block/{color}", .reversible = true, }, + .{ + .inputs = .{"4 cubyz:copper_ingot"}, + .output = "cubyz:silver_ingot", + .reversible = true, + .craftingTags = .{.alchemy}, + }, } diff --git a/src/callbacks/block/client/_list.zig b/src/callbacks/block/client/_list.zig index 9a77fb7eb6..c056756e4a 100644 --- a/src/callbacks/block/client/_list.zig +++ b/src/callbacks/block/client/_list.zig @@ -1,3 +1,4 @@ pub const edit_sign = @import("edit_sign.zig"); pub const open_chest = @import("open_chest.zig"); pub const open_window = @import("open_window.zig"); +pub const open_crafting_window = @import("open_crafting_window.zig"); diff --git a/src/callbacks/block/client/edit_sign.zig b/src/callbacks/block/client/edit_sign.zig index 12df89a0d5..8e68261ff3 100644 --- a/src/callbacks/block/client/edit_sign.zig +++ b/src/callbacks/block/client/edit_sign.zig @@ -12,7 +12,7 @@ pub fn init(_: ZonElement, _: main.callbacks.Creator) ?*anyopaque { pub fn run(_: *anyopaque, params: main.callbacks.ClientBlockCallback.Params) main.callbacks.Result { if (params.block.blockEntity() == null or !std.mem.eql(u8, params.block.blockEntity().?.id, "cubyz:sign")) { - std.log.err("Can only edit sign if block entity of the block is a sign.", .{}); + std.log.err("Error: Can only edit sign if block entity of the block is a sign.", .{}); return .ignored; } main.block_entity.block_entity_types.@"cubyz:sign".StorageClient.mutex.lock(); diff --git a/src/callbacks/block/client/open_chest.zig b/src/callbacks/block/client/open_chest.zig index 1e3df75a1c..b5bfd66fc4 100644 --- a/src/callbacks/block/client/open_chest.zig +++ b/src/callbacks/block/client/open_chest.zig @@ -12,7 +12,7 @@ pub fn init(_: ZonElement, _: main.callbacks.Creator) ?*anyopaque { pub fn run(_: *anyopaque, params: main.callbacks.ClientBlockCallback.Params) main.callbacks.Result { if (params.block.blockEntity() == null or !std.mem.eql(u8, params.block.blockEntity().?.id, "cubyz:chest")) { - std.log.err("Can only open chest if block entity of the block is a chest.", .{}); + std.log.err("Error: Can only open chest if block entity of the block is a chest.", .{}); return .ignored; } main.network.protocols.block_entity_update.sendClientDataUpdateToServer(main.game.world.?.conn, params.blockPos); diff --git a/src/callbacks/block/client/open_crafting_window.zig b/src/callbacks/block/client/open_crafting_window.zig new file mode 100644 index 0000000000..e26bc30074 --- /dev/null +++ b/src/callbacks/block/client/open_crafting_window.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +const main = @import("main"); +const Block = main.blocks.Block; +const vec = main.vec; +const Vec3i = vec.Vec3i; +const ZonElement = main.ZonElement; + +craftingTags: []main.Tag, + +pub fn init(zon: ZonElement, _: main.callbacks.Creator) ?*@This() { + const result = main.worldArena.create(@This()); + const craftingTags = main.Tag.loadTagsFromZon(main.worldArena, zon.getChild("craftingTags")); + if (craftingTags.len == 0) std.log.err("Error: Missing craftingTags \"name\" for open_crafting_window event.", .{}); + result.* = .{ + .craftingTags = craftingTags, + }; + return result; +} + +pub fn run(self: *@This(), _: main.callbacks.ClientBlockCallback.Params) main.callbacks.Result { + main.gui.windowlist.inventory_crafting.openFromCallback(self.craftingTags); + main.Window.setMouseGrabbed(false); + return .handled; +} diff --git a/src/callbacks/block/client/open_window.zig b/src/callbacks/block/client/open_window.zig index f67fa9bfc0..ea08f8722a 100644 --- a/src/callbacks/block/client/open_window.zig +++ b/src/callbacks/block/client/open_window.zig @@ -12,7 +12,7 @@ pub fn init(zon: ZonElement, _: main.callbacks.Creator) ?*@This() { const result = main.worldArena.create(@This()); result.* = .{ .windowName = main.worldArena.dupe(u8, zon.get([]const u8, "name") orelse { - std.log.err("Missing field \"name\" for open_window event.", .{}); + std.log.err("Error: Missing field \"name\" for open_window event.", .{}); return null; }), }; diff --git a/src/gui/GuiWindow.zig b/src/gui/GuiWindow.zig index e8616b0dc2..6a93e453d6 100644 --- a/src/gui/GuiWindow.zig +++ b/src/gui/GuiWindow.zig @@ -73,6 +73,8 @@ onOpenFn: *const fn () void = &defaultFunction, onCloseFn: *const fn () void = &defaultFunction, +onToggleWindowFn: *const fn () void = &defaultFunction, + var grabbedWindow: *const GuiWindow = undefined; var windowMoving: bool = false; var grabPosition: ?Vec2f = null; diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 9c85d16369..6dff31af29 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -131,7 +131,7 @@ pub fn initWindowList() void { const windowStruct = @field(windowlist, decl); windowStruct.window.id = decl; addWindow(&windowStruct.window); - const functionNames = [_][]const u8{"render", "update", "updateSelected", "updateHovered", "onOpen", "onClose"}; + const functionNames = [_][]const u8{"render", "update", "updateSelected", "updateHovered", "onOpen", "onClose", "onToggleWindow"}; inline for (functionNames) |function| { if (@hasDecl(windowStruct, function)) { @field(windowStruct.window, function ++ "Fn") = &@field(windowStruct, function); @@ -649,6 +649,7 @@ pub fn toggleGameMenu() void { _ = openWindows.swapRemove(i); window.onCloseFn(); } else { + window.onToggleWindowFn(); i += 1; } } diff --git a/src/gui/windows/inventory_crafting.zig b/src/gui/windows/inventory_crafting.zig index 9d7f78e02a..2012f308ea 100644 --- a/src/gui/windows/inventory_crafting.zig +++ b/src/gui/windows/inventory_crafting.zig @@ -35,15 +35,32 @@ const padding: f32 = 8; var availableItems: main.ListManaged(BaseItemIndex) = undefined; var itemAmount: main.ListManaged(u32) = undefined; var inventories: main.ListManaged(ClientInventory) = undefined; +var craftableFromTags: main.ListManaged(main.Tag) = undefined; +var foundNewTags: bool = false; +var defaultCraftingTags: []main.Tag = undefined; pub var arrowTexture: Texture = undefined; pub fn init() void { arrowTexture = Texture.initFromFile("assets/cubyz/ui/inventory/crafting_arrow.png"); + defaultCraftingTags = main.globalAllocator.alloc(main.Tag, 1); + defaultCraftingTags[0] = main.Tag.handCraftable; + craftableFromTags = .init(main.globalAllocator); + changeCraftingTags(defaultCraftingTags); } pub fn deinit() void { arrowTexture.deinit(); + craftableFromTags.clearAndFree(); + craftableFromTags.deinit(); + main.globalAllocator.free(defaultCraftingTags); +} + +pub fn openFromCallback(craftingTags: []main.Tag) void { + changeCraftingTags(craftingTags); + gui.closeWindowFromRef(&window); + gui.openWindowFromRef(&window); + main.Window.setMouseGrabbed(false); } fn addItemStackToAvailable(itemStack: ItemStack) void { @@ -70,7 +87,8 @@ fn findAvailableRecipes(list: *VerticalList) bool { for (0..main.game.player.inventory.size()) |i| { addItemStackToAvailable(main.game.player.inventory.getStack(i)); } - if (std.mem.eql(u32, oldAmounts, itemAmount.items)) return false; + if (std.mem.eql(u32, oldAmounts, itemAmount.items) and (!foundNewTags)) return false; + foundNewTags = false; // Remove no longer present items: var i: u32 = 0; while (i < availableItems.items.len) : (i += 1) { @@ -93,6 +111,14 @@ fn findAvailableRecipes(list: *VerticalList) bool { } continue :outer; // Ingredient not found. } + middle: for (recipe.craftingTags) |tag| { + for (craftableFromTags.items) |craftingStationTag| { + if (tag == craftingStationTag) { + continue :middle; + } + } + continue :outer; // Recipe not craftable by this. + } // All ingredients found: Add it to the list. const inv = ClientInventory.init(main.globalAllocator, recipe.sourceItems.len + 1, .{.crafting = recipe}, .other, .{}); @@ -171,6 +197,19 @@ pub fn onClose() void { inventories.deinit(); } +pub fn onToggleWindow() void { + changeCraftingTags(defaultCraftingTags); + refresh(); +} + pub fn update() void { refresh(); } + +fn changeCraftingTags(newCraftingTags: []main.Tag) void { + foundNewTags = true; + if (std.mem.eql(main.Tag, craftableFromTags.items, newCraftingTags)) return; + craftableFromTags.deinit(); + craftableFromTags = .initCapacity(main.globalAllocator, newCraftingTags.len); + craftableFromTags.appendSlice(newCraftingTags); +}