From 81d6f71d5640e36f9efd97cbfd1f13c611b0826e Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 12 Apr 2026 00:19:04 +0200 Subject: [PATCH 01/10] Add requiredTag and forbiddenTag support for item drops --- assets/cubyz/blocks/leaves/_defaults.zig.zon | 4 ++-- src/blocks.zig | 11 +++++++++-- src/items.zig | 11 ++++++++--- src/sync.zig | 13 +++++++++++++ src/tag.zig | 6 +++++- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/assets/cubyz/blocks/leaves/_defaults.zig.zon b/assets/cubyz/blocks/leaves/_defaults.zig.zon index df75ffccfe..c95244e4e3 100644 --- a/assets/cubyz/blocks/leaves/_defaults.zig.zon +++ b/assets/cubyz/blocks/leaves/_defaults.zig.zon @@ -7,8 +7,8 @@ }, }, .drops = .{ - .{.items = .{.auto}}, - .{.chance = 0.01, .items = .{"cubyz:apple"}}, + .{.items = .{.auto}, .requiredTag = .cuttable}, + .{.chance = 1.0, .items = .{"cubyz:apple"}, .forbiddenTag = .cuttable}, }, .rotation = "cubyz:decayable", .tags = .{.cuttable, .leaf}, diff --git a/src/blocks.zig b/src/blocks.zig index b8a6529083..da3062042c 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -33,6 +33,8 @@ pub const maxBlockCount: usize = 65536; // 16 bit limit pub const BlockDrop = struct { items: []const items.ItemStack, chance: f32, + forbiddenTag: ?Tag = null, + requiredTag: ?Tag = null, }; /// Ores can be found underground in veins. @@ -165,7 +167,6 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { const blockDrops = main.worldArena.alloc(BlockDrop, drops.len); for (drops, 0..) |blockDrop, i| { - blockDrops[i].chance = blockDrop.get(f32, "chance", 1); const itemZons = blockDrop.getChild("items").toSlice(); var resultItems = main.List(items.ItemStack).initCapacity(main.stackAllocator, itemZons.len); defer resultItems.deinit(); @@ -192,7 +193,13 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { const item = items.BaseItemIndex.fromId(name) orelse continue; resultItems.append(.{.item = .{.baseItem = item}, .amount = amount}); } - blockDrops[i].items = main.worldArena.dupe(main.items.ItemStack, resultItems.items); + + blockDrops[i] = BlockDrop{ + .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), + .chance = blockDrop.get(f32, "chance", 1), + .forbiddenTag = if (blockDrop.getChildOrNull("forbiddenTag")) |tagZon| Tag.loadTagFromZon(tagZon) else null, + .requiredTag = if (blockDrop.getChildOrNull("requiredTag")) |tagZon| Tag.loadTagFromZon(tagZon) else null, + }; } return blockDrops; } diff --git a/src/items.zig b/src/items.zig index 41ec77fb02..cdc5eade8f 100644 --- a/src/items.zig +++ b/src/items.zig @@ -883,11 +883,16 @@ pub const ProceduralItem = struct { // MARK: ProceduralItem return self.tooltip.items; } + pub fn hasBlockTag(self: *ProceduralItem, tag: Tag) bool { + for (self.type.blockTags()) |proceduralItemTag| { + if (proceduralItemTag == tag) return true; + } + return false; + } + pub fn isEffectiveOn(self: *ProceduralItem, block: main.blocks.Block) bool { for (block.blockTags()) |blockTag| { - for (self.type.blockTags()) |ProceduralItemTag| { - if (ProceduralItemTag == blockTag) return true; - } + if (hasBlockTag(self, blockTag)) return true; } return false; } diff --git a/src/sync.zig b/src/sync.zig index adc9aac02b..b1664f5af9 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -1479,6 +1479,7 @@ pub const Command = struct { // MARK: Command } // Apply inventory changes: + const handItem = self.source.inv.getItem(self.source.slot); // State should be stored before procedural item breaks switch (costOfChange) { .no => unreachable, .yes => {}, @@ -1499,6 +1500,18 @@ pub const Command = struct { // MARK: Command const dropAmount = self.oldBlock.mode().itemDropsOnChange(self.oldBlock, self.newBlock); for (0..dropAmount) |_| { for (self.oldBlock.blockDrops()) |drop| { + if (handItem == .proceduralItem) { + const proceduralItem = handItem.proceduralItem; + if (drop.forbiddenTag) |forbiddenTag| { + if (proceduralItem.hasBlockTag(forbiddenTag)) continue; + } + if (drop.requiredTag) |requiredTag| { + if (!proceduralItem.hasBlockTag(requiredTag)) continue; + } + } else { + if (drop.requiredTag != null) continue; + } + if (drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { self.dropLocation.drop(self.pos, self.newBlock, drop); } diff --git a/src/tag.zig b/src/tag.zig index 554d4d1490..daa180b5f3 100644 --- a/src/tag.zig +++ b/src/tag.zig @@ -37,10 +37,14 @@ pub const Tag = enum(u32) { return result; } + pub fn loadTagFromZon(zon: main.ZonElement) Tag { + return Tag.find(zon.as([]const u8, "incorrect")); + } + pub fn loadTagsFromZon(_allocator: main.heap.NeverFailingAllocator, zon: main.ZonElement) []Tag { const result = _allocator.alloc(Tag, zon.toSlice().len); for (zon.toSlice(), 0..) |tagZon, i| { - result[i] = Tag.find(tagZon.as([]const u8, "incorrect")); + result[i] = loadTagFromZon(tagZon); } return result; } From f0d6addb4b45231a7c5a8cee8dc45dec218c7b36 Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 12 Apr 2026 22:27:30 +0200 Subject: [PATCH 02/10] Allow a list of tags --- assets/cubyz/blocks/leaves/_defaults.zig.zon | 4 ++-- src/blocks.zig | 8 +++---- src/sync.zig | 23 +++++++++++++++----- src/tag.zig | 6 +---- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/assets/cubyz/blocks/leaves/_defaults.zig.zon b/assets/cubyz/blocks/leaves/_defaults.zig.zon index c95244e4e3..a0608140c5 100644 --- a/assets/cubyz/blocks/leaves/_defaults.zig.zon +++ b/assets/cubyz/blocks/leaves/_defaults.zig.zon @@ -7,8 +7,8 @@ }, }, .drops = .{ - .{.items = .{.auto}, .requiredTag = .cuttable}, - .{.chance = 1.0, .items = .{"cubyz:apple"}, .forbiddenTag = .cuttable}, + .{.items = .{.auto}, .allowedTags = .{.cuttable}}, + .{.chance = 1.0, .items = .{"cubyz:apple"}, .forbiddenTags = .{.cuttable}}, }, .rotation = "cubyz:decayable", .tags = .{.cuttable, .leaf}, diff --git a/src/blocks.zig b/src/blocks.zig index da3062042c..83732563c9 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -33,8 +33,8 @@ pub const maxBlockCount: usize = 65536; // 16 bit limit pub const BlockDrop = struct { items: []const items.ItemStack, chance: f32, - forbiddenTag: ?Tag = null, - requiredTag: ?Tag = null, + forbiddenTags: ?[]Tag = null, + allowedTags: ?[]Tag = null, }; /// Ores can be found underground in veins. @@ -197,8 +197,8 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { blockDrops[i] = BlockDrop{ .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), .chance = blockDrop.get(f32, "chance", 1), - .forbiddenTag = if (blockDrop.getChildOrNull("forbiddenTag")) |tagZon| Tag.loadTagFromZon(tagZon) else null, - .requiredTag = if (blockDrop.getChildOrNull("requiredTag")) |tagZon| Tag.loadTagFromZon(tagZon) else null, + .forbiddenTags = if (blockDrop.getChildOrNull("forbiddenTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, + .allowedTags = if (blockDrop.getChildOrNull("allowedTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, }; } return blockDrops; diff --git a/src/sync.zig b/src/sync.zig index b1664f5af9..88447dde02 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -1501,15 +1501,26 @@ pub const Command = struct { // MARK: Command for (0..dropAmount) |_| { for (self.oldBlock.blockDrops()) |drop| { if (handItem == .proceduralItem) { - const proceduralItem = handItem.proceduralItem; - if (drop.forbiddenTag) |forbiddenTag| { - if (proceduralItem.hasBlockTag(forbiddenTag)) continue; + const item = handItem.proceduralItem; + + if (drop.forbiddenTags) |tags| { + var isForbidden: bool = false; + for (tags) |tag| if (item.hasBlockTag(tag)) { + isForbidden = true; + break; + }; + if (isForbidden) continue; } - if (drop.requiredTag) |requiredTag| { - if (!proceduralItem.hasBlockTag(requiredTag)) continue; + if (drop.allowedTags) |tags| { + var hasMatch: bool = false; + for (tags) |tag| if (item.hasBlockTag(tag)) { + hasMatch = true; + break; + }; + if (!hasMatch) continue; } } else { - if (drop.requiredTag != null) continue; + if (drop.allowedTags != null) continue; } if (drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { diff --git a/src/tag.zig b/src/tag.zig index daa180b5f3..554d4d1490 100644 --- a/src/tag.zig +++ b/src/tag.zig @@ -37,14 +37,10 @@ pub const Tag = enum(u32) { return result; } - pub fn loadTagFromZon(zon: main.ZonElement) Tag { - return Tag.find(zon.as([]const u8, "incorrect")); - } - pub fn loadTagsFromZon(_allocator: main.heap.NeverFailingAllocator, zon: main.ZonElement) []Tag { const result = _allocator.alloc(Tag, zon.toSlice().len); for (zon.toSlice(), 0..) |tagZon, i| { - result[i] = loadTagFromZon(tagZon); + result[i] = Tag.find(tagZon.as([]const u8, "incorrect")); } return result; } From 86ceff8b1836de1d9c5f892995048afee65b997e Mon Sep 17 00:00:00 2001 From: Delta Date: Mon, 13 Apr 2026 19:30:53 +0200 Subject: [PATCH 03/10] Move check if item should be dropped into helper function --- src/blocks.zig | 16 ++++++++++++++++ src/sync.zig | 23 +---------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index 83732563c9..8a84cd6c4f 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -13,6 +13,7 @@ const Image = graphics.Image; const Color = graphics.Color; const TextureArray = graphics.TextureArray; const items = @import("items.zig"); +const Item = items.Item; const models = @import("models.zig"); const ModelIndex = models.ModelIndex; const rotation = @import("rotation.zig"); @@ -35,6 +36,21 @@ pub const BlockDrop = struct { chance: f32, forbiddenTags: ?[]Tag = null, allowedTags: ?[]Tag = null, + + pub fn isDroppedByItem(self: BlockDrop, item: Item) bool { + if (item != .proceduralItem) return self.allowedTags == null; + + const proceduralItem = item.proceduralItem; + if (self.forbiddenTags) |tags| { + for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return false; + } + if (self.allowedTags) |tags| { + for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return true; + return false; + } + + return true; + } }; /// Ores can be found underground in veins. diff --git a/src/sync.zig b/src/sync.zig index 88447dde02..69d01d1b03 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -1500,28 +1500,7 @@ pub const Command = struct { // MARK: Command const dropAmount = self.oldBlock.mode().itemDropsOnChange(self.oldBlock, self.newBlock); for (0..dropAmount) |_| { for (self.oldBlock.blockDrops()) |drop| { - if (handItem == .proceduralItem) { - const item = handItem.proceduralItem; - - if (drop.forbiddenTags) |tags| { - var isForbidden: bool = false; - for (tags) |tag| if (item.hasBlockTag(tag)) { - isForbidden = true; - break; - }; - if (isForbidden) continue; - } - if (drop.allowedTags) |tags| { - var hasMatch: bool = false; - for (tags) |tag| if (item.hasBlockTag(tag)) { - hasMatch = true; - break; - }; - if (!hasMatch) continue; - } - } else { - if (drop.allowedTags != null) continue; - } + if (!drop.isDroppedByItem(handItem)) continue; if (drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { self.dropLocation.drop(self.pos, self.newBlock, drop); From b11242387c059eafe1bd90b0f38ae75b9e2b4d33 Mon Sep 17 00:00:00 2001 From: Delta Date: Fri, 17 Apr 2026 10:14:26 +0200 Subject: [PATCH 04/10] Remove testing config --- assets/cubyz/blocks/leaves/_defaults.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/cubyz/blocks/leaves/_defaults.zig.zon b/assets/cubyz/blocks/leaves/_defaults.zig.zon index a0608140c5..df75ffccfe 100644 --- a/assets/cubyz/blocks/leaves/_defaults.zig.zon +++ b/assets/cubyz/blocks/leaves/_defaults.zig.zon @@ -7,8 +7,8 @@ }, }, .drops = .{ - .{.items = .{.auto}, .allowedTags = .{.cuttable}}, - .{.chance = 1.0, .items = .{"cubyz:apple"}, .forbiddenTags = .{.cuttable}}, + .{.items = .{.auto}}, + .{.chance = 0.01, .items = .{"cubyz:apple"}}, }, .rotation = "cubyz:decayable", .tags = .{.cuttable, .leaf}, From 49f0ef05a2c729ddde1b508cf41df8b7e922aeed Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 11:21:26 +0200 Subject: [PATCH 05/10] Rename helper function and rename tags to toolTags --- src/blocks.zig | 32 ++++++++++++++++---------------- src/sync.zig | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index 8a84cd6c4f..c19a18a06c 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -34,17 +34,17 @@ pub const maxBlockCount: usize = 65536; // 16 bit limit pub const BlockDrop = struct { items: []const items.ItemStack, chance: f32, - forbiddenTags: ?[]Tag = null, - allowedTags: ?[]Tag = null, + forbiddenToolTags: ?[]Tag = null, + allowedToolTags: ?[]Tag = null, - pub fn isDroppedByItem(self: BlockDrop, item: Item) bool { - if (item != .proceduralItem) return self.allowedTags == null; + pub fn isDroppedWhenBrokenWithItem(self: BlockDrop, item: Item) bool { + if (item != .proceduralItem) return self.allowedToolTags == null; const proceduralItem = item.proceduralItem; - if (self.forbiddenTags) |tags| { + if (self.forbiddenToolTags) |tags| { for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return false; } - if (self.allowedTags) |tags| { + if (self.allowedToolTags) |tags| { for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return true; return false; } @@ -86,7 +86,7 @@ var _degradable: [maxBlockCount]bool = undefined; var _viewThrough: [maxBlockCount]bool = undefined; var _alwaysViewThrough: [maxBlockCount]bool = undefined; var _hasBackFace: [maxBlockCount]bool = undefined; -var _blockTags: [maxBlockCount][]Tag = undefined; +var _tags: [maxBlockCount][]Tag = undefined; var _light: [maxBlockCount]u32 = undefined; /// How much light this block absorbs if it is transparent var _absorption: [maxBlockCount]u32 = undefined; @@ -123,13 +123,13 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 { _mode[size] = rotation.getByID(zon.get([]const u8, "rotation", "cubyz:no_rotation")); _blockHealth[size] = zon.get(f32, "blockHealth", 1); _blockResistance[size] = zon.get(f32, "blockResistance", 0); - const rotation_tags = _mode[size].getBlockTags(); + const rotation_tags = _mode[size].getTags(); const block_tags = Tag.loadTagsFromZon(main.stackAllocator, zon.getChild("tags")); defer main.stackAllocator.free(block_tags); - _blockTags[size] = std.mem.concat(main.worldArena.allocator, Tag, &.{rotation_tags, block_tags}) catch unreachable; + _tags[size] = std.mem.concat(main.worldArena.allocator, Tag, &.{rotation_tags, block_tags}) catch unreachable; - if (_blockTags[size].len == 0) std.log.err("Block {s} is missing 'tags' field", .{id}); - for (_blockTags[size]) |tag| { + if (_tags[size].len == 0) std.log.err("Block {s} is missing 'tags' field", .{id}); + for (_tags[size]) |tag| { if (tag == Tag.sbbChild) { sbb.registerChildBlock(@intCast(size), _id[size]); break; @@ -213,8 +213,8 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { blockDrops[i] = BlockDrop{ .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), .chance = blockDrop.get(f32, "chance", 1), - .forbiddenTags = if (blockDrop.getChildOrNull("forbiddenTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, - .allowedTags = if (blockDrop.getChildOrNull("allowedTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, + .forbiddenToolTags = if (blockDrop.getChildOrNull("forbiddenToolTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, + .allowedToolTags = if (blockDrop.getChildOrNull("allowedToolTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, }; } return blockDrops; @@ -433,12 +433,12 @@ pub const Block = packed struct { // MARK: Block return _hasBackFace[self.typ]; } - pub inline fn blockTags(self: Block) []const Tag { - return _blockTags[self.typ]; + pub inline fn tags(self: Block) []const Tag { + return _tags[self.typ]; } pub inline fn hasTag(self: Block, tag: Tag) bool { - return std.mem.containsAtLeastScalar(Tag, self.blockTags(), 1, tag); + return std.mem.containsAtLeastScalar(Tag, self.tags(), 1, tag); } pub inline fn light(self: Block) u32 { diff --git a/src/sync.zig b/src/sync.zig index 69d01d1b03..e4ea31dae9 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -1500,7 +1500,7 @@ pub const Command = struct { // MARK: Command const dropAmount = self.oldBlock.mode().itemDropsOnChange(self.oldBlock, self.newBlock); for (0..dropAmount) |_| { for (self.oldBlock.blockDrops()) |drop| { - if (!drop.isDroppedByItem(handItem)) continue; + if (!drop.isDroppedWhenBrokenWithItem(handItem)) continue; if (drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { self.dropLocation.drop(self.pos, self.newBlock, drop); From bec303d993d6495e464040730d1dce470e050916 Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 11:23:36 +0200 Subject: [PATCH 06/10] Rename all references to "blockTag" to "tag" --- assets/cubyz/tools/axe.zig.zon | 2 +- assets/cubyz/tools/chisel.zig.zon | 2 +- assets/cubyz/tools/pickaxe.zig.zon | 2 +- assets/cubyz/tools/shover.zig.zon | 2 +- assets/cubyz/tools/sickle.zig.zon | 2 +- mods/cubyz/rotation/stairs.zig | 6 +++--- src/blocks.zig | 4 ++-- src/gui/windows/creative_inventory.zig | 2 +- src/items.zig | 16 ++++++++-------- src/proceduralItem/modifiers/bad_at.zig | 2 +- src/proceduralItem/modifiers/good_at.zig | 2 +- src/rotation.zig | 4 ++-- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/assets/cubyz/tools/axe.zig.zon b/assets/cubyz/tools/axe.zig.zon index 069fd623c0..4f2f7cb5bd 100644 --- a/assets/cubyz/tools/axe.zig.zon +++ b/assets/cubyz/tools/axe.zig.zon @@ -1,5 +1,5 @@ .{ - .blockTags = .{.choppable}, + .tags = .{.choppable}, .disabled = .{ 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, diff --git a/assets/cubyz/tools/chisel.zig.zon b/assets/cubyz/tools/chisel.zig.zon index 065b5f38c1..05e2d5c262 100644 --- a/assets/cubyz/tools/chisel.zig.zon +++ b/assets/cubyz/tools/chisel.zig.zon @@ -1,5 +1,5 @@ .{ - .blockTags = .{.chiselable}, + .tags = .{.chiselable}, .disabled = .{ 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, diff --git a/assets/cubyz/tools/pickaxe.zig.zon b/assets/cubyz/tools/pickaxe.zig.zon index d6a5a1ac4f..5fe3fa9d2e 100644 --- a/assets/cubyz/tools/pickaxe.zig.zon +++ b/assets/cubyz/tools/pickaxe.zig.zon @@ -1,5 +1,5 @@ .{ - .blockTags = .{.mineable}, + .tags = .{.mineable}, .disabled = .{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, diff --git a/assets/cubyz/tools/shover.zig.zon b/assets/cubyz/tools/shover.zig.zon index f7f3e1e9cc..8d9b2d232e 100644 --- a/assets/cubyz/tools/shover.zig.zon +++ b/assets/cubyz/tools/shover.zig.zon @@ -1,5 +1,5 @@ .{ - .blockTags = .{.diggable}, + .tags = .{.diggable}, .disabled = .{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, diff --git a/assets/cubyz/tools/sickle.zig.zon b/assets/cubyz/tools/sickle.zig.zon index 18a7d83a94..943da75882 100644 --- a/assets/cubyz/tools/sickle.zig.zon +++ b/assets/cubyz/tools/sickle.zig.zon @@ -1,5 +1,5 @@ .{ - .blockTags = .{.cuttable}, + .tags = .{.cuttable}, .disabled = .{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, diff --git a/mods/cubyz/rotation/stairs.zig b/mods/cubyz/rotation/stairs.zig index ed0fa3c85f..b9e81c03f6 100644 --- a/mods/cubyz/rotation/stairs.zig +++ b/mods/cubyz/rotation/stairs.zig @@ -274,7 +274,7 @@ fn closestRay(comptime typ: enum { bit, intersection }, block: Block, relativePl pub fn rayIntersection(block: Block, item: main.items.Item, relativePlayerPos: Vec3f, playerDir: Vec3f) ?RayIntersectionResult { switch (item) { .proceduralItem => |proceduralItem| { - const tags = proceduralItem.type.blockTags(); + const tags = proceduralItem.type.tags(); for (tags) |tag| { if (tag == .chiselable) { return closestRay(.intersection, block, relativePlayerPos, playerDir); @@ -289,7 +289,7 @@ pub fn rayIntersection(block: Block, item: main.items.Item, relativePlayerPos: V pub fn onBlockBreaking(item: main.items.Item, relativePlayerPos: Vec3f, playerDir: Vec3f, currentData: *Block) void { switch (item) { .proceduralItem => |proceduralItem| { - for (proceduralItem.type.blockTags()) |tag| { + for (proceduralItem.type.tags()) |tag| { if (tag == .chiselable) { currentData.data |= closestRay(.bit, currentData.*, relativePlayerPos, playerDir); if (currentData.data == 255) currentData.* = .{.typ = 0, .data = 0}; @@ -311,6 +311,6 @@ pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemS return .no; } -pub fn getBlockTags() []const Tag { +pub fn getTags() []const Tag { return &.{.chiselable}; } diff --git a/src/blocks.zig b/src/blocks.zig index c19a18a06c..354a87ddd2 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -42,10 +42,10 @@ pub const BlockDrop = struct { const proceduralItem = item.proceduralItem; if (self.forbiddenToolTags) |tags| { - for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return false; + for (tags) |tag| if (proceduralItem.hasTag(tag)) return false; } if (self.allowedToolTags) |tags| { - for (tags) |tag| if (proceduralItem.hasBlockTag(tag)) return true; + for (tags) |tag| if (proceduralItem.hasTag(tag)) return true; return false; } diff --git a/src/gui/windows/creative_inventory.zig b/src/gui/windows/creative_inventory.zig index 0d0deefb6b..ff8fcc546a 100644 --- a/src/gui/windows/creative_inventory.zig +++ b/src/gui/windows/creative_inventory.zig @@ -85,7 +85,7 @@ fn initContent() void { if (searchString.len > 1 and searchString[0] == '.') { const tag = searchString[1..]; while (itemIterator.next()) |item| { - if (hasMatchingTag(item.tags(), tag) or (item.block() != null and hasMatchingTag((main.blocks.Block{.typ = item.block().?, .data = undefined}).blockTags(), tag))) { + if (hasMatchingTag(item.tags(), tag) or (item.block() != null and hasMatchingTag((main.blocks.Block{.typ = item.block().?, .data = undefined}).tags(), tag))) { items.append(Item{.baseItem = item.*}); } } diff --git a/src/items.zig b/src/items.zig index cdc5eade8f..042618f7f6 100644 --- a/src/items.zig +++ b/src/items.zig @@ -600,8 +600,8 @@ pub const ProceduralItemTypeIndex = enum(u16) { pub fn id(self: ProceduralItemTypeIndex) []const u8 { return proceduralItemTypeList.items[@intFromEnum(self)].id; } - pub fn blockTags(self: ProceduralItemTypeIndex) []const Tag { - return proceduralItemTypeList.items[@intFromEnum(self)].blockTags; + pub fn tags(self: ProceduralItemTypeIndex) []const Tag { + return proceduralItemTypeList.items[@intFromEnum(self)].tags; } pub fn properties(self: ProceduralItemTypeIndex) []const PropertyMatrix { return proceduralItemTypeList.items[@intFromEnum(self)].properties; @@ -619,7 +619,7 @@ pub const ProceduralItemTypeIndex = enum(u16) { pub const ProceduralItemType = struct { // MARK: ProceduralItemType id: []const u8, - blockTags: []main.Tag, + tags: []main.Tag, properties: []PropertyMatrix, slotInfos: [25]SlotInfo, pixelSources: [16][16]u8, @@ -883,16 +883,16 @@ pub const ProceduralItem = struct { // MARK: ProceduralItem return self.tooltip.items; } - pub fn hasBlockTag(self: *ProceduralItem, tag: Tag) bool { - for (self.type.blockTags()) |proceduralItemTag| { + pub fn hasTag(self: *ProceduralItem, tag: Tag) bool { + for (self.type.tags()) |proceduralItemTag| { if (proceduralItemTag == tag) return true; } return false; } pub fn isEffectiveOn(self: *ProceduralItem, block: main.blocks.Block) bool { - for (block.blockTags()) |blockTag| { - if (hasBlockTag(self, blockTag)) return true; + for (block.tags()) |tag| { + if (hasTag(self, tag)) return true; } return false; } @@ -1295,7 +1295,7 @@ pub fn registerProceduralItem(assetFolder: []const u8, id: []const u8, zon: ZonE const idDupe = main.worldArena.dupe(u8, id); proceduralItemTypeList.append(main.worldArena, .{ .id = idDupe, - .blockTags = Tag.loadTagsFromZon(main.worldArena, zon.getChild("blockTags")), + .tags = Tag.loadTagsFromZon(main.worldArena, zon.getChild("tags")), .slotInfos = slotInfos, .properties = parameterMatrices.toOwnedSlice(), .pixelSources = pixelSources, diff --git a/src/proceduralItem/modifiers/bad_at.zig b/src/proceduralItem/modifiers/bad_at.zig index 3acbb38677..d44e0ac928 100644 --- a/src/proceduralItem/modifiers/bad_at.zig +++ b/src/proceduralItem/modifiers/bad_at.zig @@ -17,7 +17,7 @@ pub fn combineModifiers(data1: Data, data2: Data) ?Data { } pub fn changeBlockDamage(damage: f32, block: main.blocks.Block, data: Data) f32 { - for (block.blockTags()) |tag| { + for (block.tags()) |tag| { if (tag == data.tag) return damage*(1 - data.strength); } return damage; diff --git a/src/proceduralItem/modifiers/good_at.zig b/src/proceduralItem/modifiers/good_at.zig index 275a8f63da..1c5a679c97 100644 --- a/src/proceduralItem/modifiers/good_at.zig +++ b/src/proceduralItem/modifiers/good_at.zig @@ -17,7 +17,7 @@ pub fn combineModifiers(data1: Data, data2: Data) ?Data { } pub fn changeBlockDamage(damage: f32, block: main.blocks.Block, data: Data) f32 { - for (block.blockTags()) |tag| { + for (block.tags()) |tag| { if (tag == data.tag) return damage*(1 + data.strength); } return damage; diff --git a/src/rotation.zig b/src/rotation.zig index 9824dcc136..64641fd529 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -127,7 +127,7 @@ pub const RotationMode = struct { // MARK: RotationMode if (newBlock.typ != oldBlock.typ) return 1; return 0; } - pub fn getBlockTags() []const Tag { + pub fn getTags() []const Tag { return &.{}; } pub fn formatBlockData(block: Block, _list: *main.List(u8)) void { @@ -172,7 +172,7 @@ pub const RotationMode = struct { // MARK: RotationMode itemDropsOnChange: *const fn (oldBlock: Block, newBlock: Block) u16 = DefaultFunctions.itemDropsOnChange, - getBlockTags: *const fn () []const Tag = DefaultFunctions.getBlockTags, + getTags: *const fn () []const Tag = DefaultFunctions.getTags, formatBlockData: *const fn (block: Block, _list: *main.List(u8)) void = DefaultFunctions.formatBlockData, }; From 552404a357abcd2045261597dc840f3073aa5b4c Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 19:31:59 +0200 Subject: [PATCH 07/10] Re-introduce blockTags as concept when it's about blocks --- mods/cubyz/rotation/stairs.zig | 2 +- src/blocks.zig | 2 +- src/rotation.zig | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/cubyz/rotation/stairs.zig b/mods/cubyz/rotation/stairs.zig index b9e81c03f6..bcea9ab6cf 100644 --- a/mods/cubyz/rotation/stairs.zig +++ b/mods/cubyz/rotation/stairs.zig @@ -311,6 +311,6 @@ pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemS return .no; } -pub fn getTags() []const Tag { +pub fn getBlockTags() []const Tag { return &.{.chiselable}; } diff --git a/src/blocks.zig b/src/blocks.zig index 354a87ddd2..e685ed09c4 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -123,7 +123,7 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 { _mode[size] = rotation.getByID(zon.get([]const u8, "rotation", "cubyz:no_rotation")); _blockHealth[size] = zon.get(f32, "blockHealth", 1); _blockResistance[size] = zon.get(f32, "blockResistance", 0); - const rotation_tags = _mode[size].getTags(); + const rotation_tags = _mode[size].getBlockTags(); const block_tags = Tag.loadTagsFromZon(main.stackAllocator, zon.getChild("tags")); defer main.stackAllocator.free(block_tags); _tags[size] = std.mem.concat(main.worldArena.allocator, Tag, &.{rotation_tags, block_tags}) catch unreachable; diff --git a/src/rotation.zig b/src/rotation.zig index 64641fd529..9824dcc136 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -127,7 +127,7 @@ pub const RotationMode = struct { // MARK: RotationMode if (newBlock.typ != oldBlock.typ) return 1; return 0; } - pub fn getTags() []const Tag { + pub fn getBlockTags() []const Tag { return &.{}; } pub fn formatBlockData(block: Block, _list: *main.List(u8)) void { @@ -172,7 +172,7 @@ pub const RotationMode = struct { // MARK: RotationMode itemDropsOnChange: *const fn (oldBlock: Block, newBlock: Block) u16 = DefaultFunctions.itemDropsOnChange, - getTags: *const fn () []const Tag = DefaultFunctions.getTags, + getBlockTags: *const fn () []const Tag = DefaultFunctions.getBlockTags, formatBlockData: *const fn (block: Block, _list: *main.List(u8)) void = DefaultFunctions.formatBlockData, }; From 50efdd2ba1ce780a08766c6abde4dd91607655b9 Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 20:22:03 +0200 Subject: [PATCH 08/10] Cleaner syntax --- src/blocks.zig | 2 +- src/items.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index e685ed09c4..43d3a21776 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -210,7 +210,7 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { resultItems.append(.{.item = .{.baseItem = item}, .amount = amount}); } - blockDrops[i] = BlockDrop{ + blockDrops[i] = .{ .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), .chance = blockDrop.get(f32, "chance", 1), .forbiddenToolTags = if (blockDrop.getChildOrNull("forbiddenToolTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, diff --git a/src/items.zig b/src/items.zig index 042618f7f6..0bf41d4611 100644 --- a/src/items.zig +++ b/src/items.zig @@ -892,7 +892,7 @@ pub const ProceduralItem = struct { // MARK: ProceduralItem pub fn isEffectiveOn(self: *ProceduralItem, block: main.blocks.Block) bool { for (block.tags()) |tag| { - if (hasTag(self, tag)) return true; + if (self.hasTag(tag)) return true; } return false; } From bd651f37ded6611dd9c56dfbbccbf9ecd0f0d875 Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 20:36:34 +0200 Subject: [PATCH 09/10] Make forbiddenToolTags non-null, and log error if allowedToolTags is an empty array --- src/blocks.zig | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index 43d3a21776..4aece48bc2 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -34,16 +34,14 @@ pub const maxBlockCount: usize = 65536; // 16 bit limit pub const BlockDrop = struct { items: []const items.ItemStack, chance: f32, - forbiddenToolTags: ?[]Tag = null, + forbiddenToolTags: []Tag, allowedToolTags: ?[]Tag = null, pub fn isDroppedWhenBrokenWithItem(self: BlockDrop, item: Item) bool { if (item != .proceduralItem) return self.allowedToolTags == null; const proceduralItem = item.proceduralItem; - if (self.forbiddenToolTags) |tags| { - for (tags) |tag| if (proceduralItem.hasTag(tag)) return false; - } + for (self.forbiddenToolTags) |tag| if (proceduralItem.hasTag(tag)) return false; if (self.allowedToolTags) |tags| { for (tags) |tag| if (proceduralItem.hasTag(tag)) return true; return false; @@ -210,11 +208,20 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { resultItems.append(.{.item = .{.baseItem = item}, .amount = amount}); } + var allowedToolTags: ?[]Tag = null; + if (blockDrop.getChildOrNull("allowedToolTags")) |tagZon| { + const tags = Tag.loadTagsFromZon(main.stackAllocator, tagZon); + if (tags.len == 0) { + std.log.err("Field '.allowedToolTags' is an empty array. No tool can drop this blockDrop", .{}); + } + allowedToolTags = tags; + } + blockDrops[i] = .{ .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), .chance = blockDrop.get(f32, "chance", 1), - .forbiddenToolTags = if (blockDrop.getChildOrNull("forbiddenToolTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, - .allowedToolTags = if (blockDrop.getChildOrNull("allowedToolTags")) |tagZon| Tag.loadTagsFromZon(main.stackAllocator, tagZon) else null, + .forbiddenToolTags = Tag.loadTagsFromZon(main.stackAllocator, blockDrop.getChild("forbiddenToolTags")), + .allowedToolTags = allowedToolTags, }; } return blockDrops; From 4ee3db2e930f1f815728c5492cb56fca788da0ca Mon Sep 17 00:00:00 2001 From: Delta Date: Sun, 19 Apr 2026 21:03:30 +0200 Subject: [PATCH 10/10] Use worldArena as allocator --- src/blocks.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blocks.zig b/src/blocks.zig index 4aece48bc2..0d1a386733 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -210,7 +210,7 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { var allowedToolTags: ?[]Tag = null; if (blockDrop.getChildOrNull("allowedToolTags")) |tagZon| { - const tags = Tag.loadTagsFromZon(main.stackAllocator, tagZon); + const tags = Tag.loadTagsFromZon(main.worldArena, tagZon); if (tags.len == 0) { std.log.err("Field '.allowedToolTags' is an empty array. No tool can drop this blockDrop", .{}); } @@ -220,7 +220,7 @@ pub fn loadBlockDrop(blockId: ?[]const u8, zon: ZonElement) []const BlockDrop { blockDrops[i] = .{ .items = main.worldArena.dupe(main.items.ItemStack, resultItems.items), .chance = blockDrop.get(f32, "chance", 1), - .forbiddenToolTags = Tag.loadTagsFromZon(main.stackAllocator, blockDrop.getChild("forbiddenToolTags")), + .forbiddenToolTags = Tag.loadTagsFromZon(main.worldArena, blockDrop.getChild("forbiddenToolTags")), .allowedToolTags = allowedToolTags, }; }