diff --git a/src/server/terrain/simple_structures/SbbGen.zig b/src/server/terrain/simple_structures/SbbGen.zig index 07a592e2..0cab4a62 100644 --- a/src/server/terrain/simple_structures/SbbGen.zig +++ b/src/server/terrain/simple_structures/SbbGen.zig @@ -20,36 +20,47 @@ const SbbGen = @This(); structureRef: *const sbb.StructureBuildingBlock, placeMode: Blueprint.PasteMode, +rotation: sbb.Rotation, pub fn getHash(self: SbbGen) u64 { return std.hash.Wyhash.hash(@intFromEnum(self.placeMode), self.structureRef.id); } pub fn loadModel(arenaAllocator: NeverFailingAllocator, parameters: ZonElement) *SbbGen { - const structureId = parameters.get(?[]const u8, "structure", null) orelse unreachable; + const structureId = parameters.get(?[]const u8, "structure", null) orelse { + main.utils.panicWithMessage("Error loading generator 'cubyz:sbb' structure field is mandatory.", .{}); + }; const structureRef = sbb.getByStringId(structureId) orelse { - std.log.err("Could not find structure building block with id '{s}'", .{structureId}); - unreachable; + main.utils.panicWithMessage("Could not find structure building block with id '{s}'", .{structureId}); + }; + const rotationParam = parameters.getChild("rotation"); + const rotation = sbb.Rotation.fromZon(rotationParam) catch |err| blk: { + switch(err) { + error.UnknownString => std.log.err("Error loading generator 'cubyz:sbb' structure '{s}': Specified unknown rotation '{s}'", .{structureId, rotationParam.as([]const u8, "")}), + error.UnknownType => std.log.err("Error loading generator 'cubyz:sbb' structure '{s}': Unsupported type of rotation field '{s}'", .{structureId, @tagName(rotationParam)}), + } + break :blk .random; }; const self = arenaAllocator.create(SbbGen); self.* = .{ .structureRef = structureRef, .placeMode = std.meta.stringToEnum(Blueprint.PasteMode, parameters.get([]const u8, "placeMode", "degradable")) orelse Blueprint.PasteMode.degradable, + .rotation = rotation, }; return self; } pub fn generate(self: *SbbGen, _: GenerationMode, x: i32, y: i32, z: i32, chunk: *ServerChunk, _: CaveMapView, _: CaveBiomeMapView, seed: *u64, _: bool) void { - placeSbb(self, self.structureRef, Vec3i{x, y, z}, Neighbor.dirUp, chunk, seed); + placeSbb(self, self.structureRef, Vec3i{x, y, z}, Neighbor.dirUp, self.rotation.getInitialRotation(seed), chunk, seed); } -fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placementPosition: Vec3i, placementDirection: Neighbor, chunk: *ServerChunk, seed: *u64) void { +fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placementPosition: Vec3i, placementDirection: Neighbor, rotation: sbb.Rotation, chunk: *ServerChunk, seed: *u64) void { const origin = structure.blueprints[0].originBlock; - const rotationCount = alignDirections(origin.direction(), placementDirection) catch |err| { + const blueprintRotation = rotation.apply(alignDirections(origin.direction(), placementDirection) catch |err| { std.log.err("Could not align directions for structure '{s}' for directions '{s}'' and '{s}', error: {s}", .{structure.id, @tagName(origin.direction()), @tagName(placementDirection), @errorName(err)}); return; - }; - const rotated = &structure.blueprints[rotationCount]; + }); + const rotated = &structure.blueprints[@intFromEnum(blueprintRotation)]; const rotatedOrigin = rotated.originBlock.pos(); const pastePosition = placementPosition - rotatedOrigin - placementDirection.relPos(); @@ -57,19 +68,13 @@ fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placeme for(rotated.childBlocks) |childBlock| { const child = structure.pickChild(childBlock, seed) orelse continue; - placeSbb(self, child, pastePosition + childBlock.pos(), childBlock.direction(), chunk, seed); + const childRotation = rotation.getChildRotation(seed, child.rotation, childBlock.direction()); + placeSbb(self, child, pastePosition + childBlock.pos(), childBlock.direction(), childRotation, chunk, seed); } } -fn alignDirections(input: Neighbor, desired: Neighbor) !usize { - const Rotation = enum(u3) { - @"0" = 0, - @"90" = 1, - @"180" = 2, - @"270" = 3, - NotPossibleToAlign = 4, - }; - comptime var alignTable: [6][6]Rotation = undefined; +fn alignDirections(input: Neighbor, desired: Neighbor) !sbb.Rotation.FixedRotation { + comptime var alignTable: [6][6]error{NotPossibleToAlign}!sbb.Rotation.FixedRotation = undefined; comptime for(Neighbor.iterable) |in| { for(Neighbor.iterable) |out| blk: { var current = in; @@ -80,11 +85,8 @@ fn alignDirections(input: Neighbor, desired: Neighbor) !usize { } current = current.rotateZ(); } - alignTable[in.toInt()][out.toInt()] = Rotation.NotPossibleToAlign; + alignTable[in.toInt()][out.toInt()] = error.NotPossibleToAlign; } }; - switch(alignTable[input.toInt()][desired.toInt()]) { - .NotPossibleToAlign => return error.NotPossibleToAlign, - else => |v| return @intFromEnum(v), - } + return alignTable[input.toInt()][desired.toInt()]; } diff --git a/src/server/terrain/structure_building_blocks.zig b/src/server/terrain/structure_building_blocks.zig index 01d98011..f4212143 100644 --- a/src/server/terrain/structure_building_blocks.zig +++ b/src/server/terrain/structure_building_blocks.zig @@ -114,10 +114,78 @@ pub fn isOriginBlock(block: Block) bool { return block.typ == originBlockNumericId; } +pub const RotationMode = enum { + fixed, + random, + inherit, +}; + +pub const Rotation = union(RotationMode) { + fixed: FixedRotation, + random: void, + inherit: void, + + pub const FixedRotation = enum(u2) { + @"0" = 0, + @"90" = 1, + @"180" = 2, + @"270" = 3, + }; + + pub fn apply(self: Rotation, rotation: FixedRotation) FixedRotation { + return switch(self) { + .fixed => |fixed| @enumFromInt(@intFromEnum(rotation) +% @intFromEnum(fixed)), + .random, .inherit => rotation, + }; + } + pub fn getInitialRotation(self: Rotation, seed: *u64) Rotation { + return switch(self) { + .fixed => self, + .random => sampleRandom(seed), + .inherit => .{.fixed = .@"0"}, + }; + } + fn sampleRandom(seed: *u64) Rotation { + return .{.fixed = @enumFromInt(main.random.nextInt(u2, seed))}; + } + pub fn getChildRotation(self: Rotation, seed: *u64, child: Rotation, direction: Neighbor) Rotation { + return switch(direction) { + .dirDown, .dirUp => switch(child) { + .random => sampleRandom(seed), + .inherit => self, + else => |r| r, + }, + else => .{.fixed = .@"0"}, + }; + } + pub fn fromZon(zon: ZonElement) error{UnknownString, UnknownType}!Rotation { + return switch(zon) { + .string, .stringOwned => |str| { + if(std.meta.stringToEnum(FixedRotation, str)) |r| { + return .{.fixed = r}; + } + if(std.meta.stringToEnum(RotationMode, str)) |mode| { + return switch(mode) { + .fixed => .{.fixed = .@"0"}, + .random => .{.random = {}}, + .inherit => .{.inherit = {}}, + }; + } + return error.UnknownString; + }, + .int => |value| .{.fixed = @enumFromInt(@abs(@divTrunc(value, 90))%4)}, + .float => |value| .{.fixed = @enumFromInt(@abs(@as(u64, @intFromFloat(value/90.0)))%4)}, + .null => Rotation.random, + else => return error.UnknownType, + }; + } +}; + pub const StructureBuildingBlock = struct { id: []const u8, children: []AliasTable(Child), blueprints: *[4]BlueprintEntry, + rotation: Rotation, fn initFromZon(stringId: []const u8, zon: ZonElement) !StructureBuildingBlock { const blueprintId = zon.get(?[]const u8, "blueprint", null) orelse { @@ -128,6 +196,14 @@ pub const StructureBuildingBlock = struct { std.log.err("['{s}'] Could not find blueprint '{s}'.", .{stringId, blueprintId}); return error.MissingBlueprint; }; + const rotationParam = zon.getChild("rotation"); + const rotation = Rotation.fromZon(rotationParam) catch |err| blk: { + switch(err) { + error.UnknownString => std.log.err("['{s}'] specified unknown rotation '{s}'", .{stringId, rotationParam.as([]const u8, "")}), + error.UnknownType => std.log.err("['{s}'] unsupported type of rotation field '{s}'", .{stringId, @tagName(rotationParam)}), + } + break :blk .inherit; + }; const blueprints = arenaAllocator.create([4]BlueprintEntry); blueprints.* = blueprintsTemplate.*; @@ -136,6 +212,7 @@ pub const StructureBuildingBlock = struct { .id = stringId, .children = arenaAllocator.alloc(AliasTable(Child), childBlockStringId.items.len), .blueprints = blueprints, + .rotation = rotation, }; const childrenZon = zon.getChild("children"); for(childBlockStringId.items, 0..) |colorName, colorIndex| { @@ -186,6 +263,7 @@ pub const StructureBuildingBlock = struct { return .{ .id = sbbId, .children = &.{}, + .rotation = .inherit, .blueprints = blueprints, }; } diff --git a/src/utils.zig b/src/utils.zig index 263a3885..33c9621d 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -2213,7 +2213,7 @@ test "NamedCallbacks registers functions" { try std.testing.expectEqual(null, testFunctions.getFunctionPointer("wrongSignatureFunction")); } -pub fn panicWithMessage(comptime fmt: []const u8, args: anytype) void { +pub fn panicWithMessage(comptime fmt: []const u8, args: anytype) noreturn { const message = std.fmt.allocPrint(main.stackAllocator.allocator, fmt, args) catch unreachable; @panic(message); }