diff --git a/src/blueprint.zig b/src/blueprint.zig index 25daff04..28bcd745 100644 --- a/src/blueprint.zig +++ b/src/blueprint.zig @@ -10,6 +10,8 @@ const Array3D = main.utils.Array3D; const Block = main.blocks.Block; const NeverFailingAllocator = main.heap.NeverFailingAllocator; const User = main.server.User; +const ServerChunk = main.chunk.ServerChunk; +const Degrees = main.rotation.Degrees; const GameIdToBlueprintIdMapType = std.AutoHashMap(u16, u16); const BlockIdSizeType = u32; @@ -36,6 +38,32 @@ pub const Blueprint = struct { pub fn clone(self: *Blueprint, allocator: NeverFailingAllocator) Blueprint { return .{.blocks = self.blocks.clone(allocator)}; } + pub fn rotateZ(self: Blueprint, allocator: NeverFailingAllocator, angle: Degrees) Blueprint { + var new = Blueprint{ + .blocks = switch(angle) { + .@"0", .@"180" => .init(allocator, self.blocks.width, self.blocks.depth, self.blocks.height), + .@"90", .@"270" => .init(allocator, self.blocks.depth, self.blocks.width, self.blocks.height), + }, + }; + + for(0..self.blocks.width) |xOld| { + for(0..self.blocks.depth) |yOld| { + const xNew, const yNew = switch(angle) { + .@"0" => .{xOld, yOld}, + .@"90" => .{new.blocks.width - yOld - 1, xOld}, + .@"180" => .{new.blocks.width - xOld - 1, new.blocks.depth - yOld - 1}, + .@"270" => .{yOld, new.blocks.depth - xOld - 1}, + }; + + for(0..self.blocks.height) |z| { + const block = self.blocks.get(xOld, yOld, z); + new.blocks.set(xNew, yNew, z, block.rotateZ(angle)); + } + } + } + return new; + } + const CaptureResult = union(enum) { success: Blueprint, failure: struct {pos: Vec3i, message: []const u8}, diff --git a/src/rotation.zig b/src/rotation.zig index e1958cec..acafccc2 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -806,7 +806,7 @@ pub const RotationModes = struct { comptime var rotationTable: [4][256]u8 = undefined; comptime for(0..4) |a| { for(0..256) |old| { - var new: u8 = 0; + var new: u8 = 0b11_11_11_11; for(0..2) |i| for(0..2) |j| for(0..2) |k| { const sin: f32 = @sin((std.math.pi/2.0)*@as(f32, @floatFromInt(a))); @@ -819,7 +819,7 @@ pub const RotationModes = struct { const rY = @intFromBool(x*sin + y*cos > 0); if(hasSubBlock(@intCast(old), @intCast(i), @intCast(j), @intCast(k))) { - new |= subBlockMask(rX, rY, @intCast(k)); + new &= ~subBlockMask(rX, rY, @intCast(k)); } }; rotationTable[a][old] = new; diff --git a/src/server/command/_list.zig b/src/server/command/_list.zig index afee7f90..9b5646ca 100644 --- a/src/server/command/_list.zig +++ b/src/server/command/_list.zig @@ -12,3 +12,4 @@ pub const deselect = @import("worldedit/deselect.zig"); pub const copy = @import("worldedit/copy.zig"); pub const paste = @import("worldedit/paste.zig"); pub const blueprint = @import("worldedit/blueprint.zig"); +pub const rotate = @import("worldedit/rotate.zig"); diff --git a/src/server/command/worldedit/rotate.zig b/src/server/command/worldedit/rotate.zig new file mode 100644 index 00000000..f508b57a --- /dev/null +++ b/src/server/command/worldedit/rotate.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +const main = @import("root"); +const User = main.server.User; +const Degrees = main.rotation.Degrees; + +pub const description = "rotate clipboard content around Z axis counterclockwise."; +pub const usage = + \\/rotate + \\/rotate <0/90/180/270> +; + +pub fn execute(args: []const u8, source: *User) void { + var angle: Degrees = .@"90"; + if(args.len != 0) { + angle = std.meta.stringToEnum(Degrees, args) orelse { + source.sendMessage("#ff0000Error: Invalid angle '{s}'. Use 0, 90, 180 or 270.", .{args}); + return; + }; + } + if(source.worldEditData.clipboard == null) { + source.sendMessage("#ff0000Error: No clipboard content to rotate.", .{}); + return; + } + const current = source.worldEditData.clipboard.?; + defer current.deinit(main.globalAllocator); + source.worldEditData.clipboard = current.rotateZ(main.globalAllocator, angle); +}