diff --git a/assets/cubyz/blocks/duckweed.zig.zon b/assets/cubyz/blocks/duckweed.zig.zon new file mode 100644 index 00000000..4ba7afd8 --- /dev/null +++ b/assets/cubyz/blocks/duckweed.zig.zon @@ -0,0 +1,24 @@ +.{ + .tags = .{.leaf}, + .blockHealth = 0.2, + .drops = .{ + .{.items = .{.auto}}, + }, + .degradable = true, + .collide = false, + .alwaysViewThrough = true, + .absorbedLight = 0x000000, + .model = .{ + .model = "cubyz:plane", + .states = 4, + }, + .rotation = .texturePile, + .texture0 = "cubyz:duckweed/0", + .texture1 = "cubyz:duckweed/1", + .texture2 = "cubyz:duckweed/2", + .texture3 = "cubyz:duckweed/3", + .item = .{ + .texture = "duckweed.png", + }, + .lodReplacement = "cubyz:air", +} diff --git a/assets/cubyz/blocks/textures/duckweed/0.png b/assets/cubyz/blocks/textures/duckweed/0.png new file mode 100644 index 00000000..6d415a0a Binary files /dev/null and b/assets/cubyz/blocks/textures/duckweed/0.png differ diff --git a/assets/cubyz/blocks/textures/duckweed/1.png b/assets/cubyz/blocks/textures/duckweed/1.png new file mode 100644 index 00000000..adb39858 Binary files /dev/null and b/assets/cubyz/blocks/textures/duckweed/1.png differ diff --git a/assets/cubyz/blocks/textures/duckweed/2.png b/assets/cubyz/blocks/textures/duckweed/2.png new file mode 100644 index 00000000..5faf7a81 Binary files /dev/null and b/assets/cubyz/blocks/textures/duckweed/2.png differ diff --git a/assets/cubyz/blocks/textures/duckweed/3.png b/assets/cubyz/blocks/textures/duckweed/3.png new file mode 100644 index 00000000..c45b5c34 Binary files /dev/null and b/assets/cubyz/blocks/textures/duckweed/3.png differ diff --git a/assets/cubyz/items/textures/duckweed.png b/assets/cubyz/items/textures/duckweed.png new file mode 100644 index 00000000..370ad91e Binary files /dev/null and b/assets/cubyz/items/textures/duckweed.png differ diff --git a/src/blocks.zig b/src/blocks.zig index 8c04acb5..4e74d3d7 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -106,6 +106,7 @@ var _absorption: [maxBlockCount]u32 = undefined; /// GUI that is opened on click. var _gui: [maxBlockCount][]u8 = undefined; var _mode: [maxBlockCount]*RotationMode = undefined; +var _modeData: [maxBlockCount]u16 = undefined; var _lodReplacement: [maxBlockCount]u16 = undefined; var _opaqueVariant: [maxBlockCount]u16 = undefined; var _friction: [maxBlockCount]f32 = undefined; @@ -364,6 +365,10 @@ pub const Block = packed struct { // MARK: Block return _mode[self.typ]; } + pub inline fn modeData(self: Block) u16 { + return _modeData[self.typ]; + } + pub inline fn rotateZ(self: Block, angle: Degrees) Block { return .{.typ = self.typ, .data = self.mode().rotateZ(self.data, angle)}; } @@ -666,7 +671,7 @@ pub const meshes = struct { // MARK: meshes } pub fn register(assetFolder: []const u8, _: []const u8, zon: ZonElement) void { - _modelIndex[meshes.size] = _mode[meshes.size].createBlockModel(zon.getChild("model")); + _modelIndex[meshes.size] = _mode[meshes.size].createBlockModel(.{.typ = @intCast(meshes.size), .data = 0}, &_modeData[meshes.size], zon.getChild("model")); // The actual model is loaded later, in the rendering thread. // But textures can be loaded here: diff --git a/src/rotation.zig b/src/rotation.zig index acafccc2..028812e7 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -41,7 +41,7 @@ pub const RotationMode = struct { // MARK: RotationMode fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, _: ?Neighbor, _: *Block, _: Block, blockPlacing: bool) bool { return blockPlacing; } - fn createBlockModel(zon: ZonElement) ModelIndex { + fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { return main.models.getModelIndex(zon.as([]const u8, "cubyz:cube")); } fn updateData(_: *Block, _: Neighbor, _: Block) bool { @@ -126,7 +126,7 @@ pub const RotationMode = struct { // MARK: RotationMode // Rotates block data counterclockwise around the Z axis. rotateZ: *const fn(data: u16, angle: Degrees) u16 = DefaultFunctions.rotateZ, - createBlockModel: *const fn(zon: ZonElement) ModelIndex = &DefaultFunctions.createBlockModel, + createBlockModel: *const fn(block: Block, modeData: *u16, zon: ZonElement) ModelIndex = &DefaultFunctions.createBlockModel, /// Updates the block data of a block in the world or places a block in the world. /// return true if the placing was successful, false otherwise. @@ -176,7 +176,7 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; @@ -235,7 +235,7 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; @@ -355,7 +355,7 @@ pub const RotationModes = struct { } } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(fenceModels.get(modelId)) |modelIndex| return modelIndex; @@ -638,7 +638,7 @@ pub const RotationModes = struct { }; } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const radius = zon.get(f32, "radius", 4); if(branchModels.get(@bitCast(radius))) |modelIndex| return modelIndex; @@ -900,7 +900,7 @@ pub const RotationModes = struct { return mem[0..faces]; } - pub fn createBlockModel(_: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, _: ZonElement) ModelIndex { if(modelIndex) |idx| return idx; for(0..256) |i| { var quads = main.List(main.models.QuadInfo).init(main.stackAllocator); @@ -1142,7 +1142,7 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const baseModelId: []const u8 = zon.get([]const u8, "base", "cubyz:cube"); const sideModelId: []const u8 = zon.get([]const u8, "side", "cubyz:cube"); const key: []const u8 = std.mem.concat(main.stackAllocator.allocator, u8, &.{baseModelId, sideModelId}) catch unreachable; @@ -1362,7 +1362,7 @@ pub const RotationModes = struct { rotatedModels.clearRetainingCapacity(); } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; @@ -1490,7 +1490,7 @@ pub const RotationModes = struct { modelCache = null; } - pub fn createBlockModel(zon: ZonElement) ModelIndex { + pub fn createBlockModel(_: Block, _: *u16, zon: ZonElement) ModelIndex { const modelId = zon.as([]const u8, "cubyz:cube"); if(!std.mem.eql(u8, modelId, "cubyz:cube")) { std.log.err("Ores can only be use on cube models.", .{modelId}); @@ -1540,6 +1540,88 @@ pub const RotationModes = struct { currentData.data = 0; } }; + pub const TexturePile = struct { + pub const id: []const u8 = "texturePile"; + var rotatedModels: std.StringHashMap(ModelIndex) = undefined; + + fn init() void { + rotatedModels = .init(main.globalAllocator.allocator); + } + + fn deinit() void { + rotatedModels.deinit(); + } + + fn reset() void { + rotatedModels.clearRetainingCapacity(); + } + + fn transform(quad: *main.models.QuadInfo, data: u16) void { + quad.textureSlot = data%16; + } + + pub fn createBlockModel(block: Block, modeData: *u16, zon: ZonElement) ModelIndex { + const modelId = zon.get([]const u8, "model", "cubyz:cube"); + const stateCount = zon.get(u16, "states", 2); + const blockId = block.id(); + if(stateCount <= 1) { + std.log.err("Block '{s}' uses texture pile with {} states. 'texturePile' should have at least 2 states, use 'no_rotation' instead", .{blockId, stateCount}); + } else if(stateCount > 16) { + std.log.err("Block '{s}' uses texture pile with {} states. 'texturePile' can have at most 16 states.", .{blockId, stateCount}); + } + modeData.* = stateCount; + + if(rotatedModels.get(modelId)) |modelIndex| return modelIndex; + + const baseModel = main.models.getModelIndex(modelId).model(); + + const modelIndex = baseModel.transformModel(transform, .{@as(u16, @intCast(0))}); + for(1..16) |data| { + _ = baseModel.transformModel(transform, .{@as(u16, @intCast(data))}); + } + rotatedModels.put(modelId, modelIndex) catch unreachable; + return modelIndex; + } + + pub fn model(block: Block) ModelIndex { + return .{.index = blocks.meshes.modelIndexStart(block).index + @min(block.data, block.modeData() - 1)}; + } + + pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, _: ?Neighbor, currentData: *Block, _: Block, blockPlacing: bool) bool { + if(blockPlacing) { + currentData.data = 0; + return true; + } + if(currentData.data >= currentData.modeData() - 1) { + return false; + } + currentData.data = currentData.data + 1; + return true; + } + + pub fn onBlockBreaking(_: ?main.items.Item, _: Vec3f, _: Vec3f, currentData: *Block) void { + if(currentData.data == 0) { + currentData.* = .{.typ = 0, .data = 0}; + } else { + currentData.data = currentData.data - 1; + } + } + + pub fn canBeChangedInto(oldBlock: Block, newBlock: Block, item: main.items.ItemStack, shouldDropSourceBlockOnSuccess: *bool) RotationMode.CanBeChangedInto { + switch(RotationMode.DefaultFunctions.canBeChangedInto(oldBlock, newBlock, item, shouldDropSourceBlockOnSuccess)) { + .no, .yes_costsDurability, .yes_dropsItems => return .no, + .yes, .yes_costsItems => { + const amountChange = @as(i32, newBlock.data) - if(oldBlock.typ == newBlock.typ) @as(i32, oldBlock.data) else 0; + if(amountChange <= 0) { + return .{.yes_dropsItems = @intCast(-amountChange)}; + } else { + if(item.item == null or item.item.? != .baseItem or item.item.?.baseItem.block != newBlock.typ) return .no; + return .{.yes_costsItems = @intCast(amountChange)}; + } + }, + } + } + }; }; // MARK: init/register