diff --git a/MCGalaxy/Commands/building/CmdSpin.cs b/MCGalaxy/Commands/building/CmdSpin.cs index 315e2d141..d2e706f6d 100644 --- a/MCGalaxy/Commands/building/CmdSpin.cs +++ b/MCGalaxy/Commands/building/CmdSpin.cs @@ -35,16 +35,17 @@ namespace MCGalaxy.Commands.Building { Player.Message(p, "You haven't copied anything yet"); return; } string opt = message.ToLower(); + BlockDefinition[] defs = p.level.CustomBlockDefs; // Mirroring if (opt == "mirrorx" || opt == "mirror x") { - Flip.MirrorX(p.CopyBuffer); + Flip.MirrorX(p.CopyBuffer, defs); Player.Message(p, "Flipped copy across the X (east/west) axis."); } else if (opt == "mirrory" || opt == "mirror y" || opt == "u") { - Flip.MirrorY(p.CopyBuffer); + Flip.MirrorY(p.CopyBuffer, defs); Player.Message(p, "Flipped copy across the Y (vertical) axis."); } else if (opt == "mirrorz" || opt == "mirror z" || opt == "m") { - Flip.MirrorZ(p.CopyBuffer); + Flip.MirrorZ(p.CopyBuffer, defs); Player.Message(p, "Flipped copy across the Z (north/south) axis."); } else { string[] args = opt.SplitSpaces(); @@ -55,11 +56,11 @@ namespace MCGalaxy.Commands.Building { if (angle == 0) { } else if (axis == 'X') { - p.CopyBuffer = Flip.RotateX(p.CopyBuffer, angle); + p.CopyBuffer = Flip.RotateX(p.CopyBuffer, angle, defs); } else if (axis == 'Y') { - p.CopyBuffer = Flip.RotateY(p.CopyBuffer, angle); + p.CopyBuffer = Flip.RotateY(p.CopyBuffer, angle, defs); } else if (axis == 'Z') { - p.CopyBuffer = Flip.RotateZ(p.CopyBuffer, angle); + p.CopyBuffer = Flip.RotateZ(p.CopyBuffer, angle, defs); } Player.Message(p, "Rotated copy {0} degrees around the {1} axis", angle, axis); } diff --git a/MCGalaxy/Drawing/Flip.cs b/MCGalaxy/Drawing/Flip.cs index ac580841c..fbc5abd50 100644 --- a/MCGalaxy/Drawing/Flip.cs +++ b/MCGalaxy/Drawing/Flip.cs @@ -20,46 +20,69 @@ using System.IO; namespace MCGalaxy.Drawing { public static class Flip { - - public static CopyState RotateX(CopyState state, int angle) { + + static string[] rotX_90_270 = new string[] { "NS", "UD" }; + static string[] rotX_180 = new string[] { "N", "S", "NE", "SE", "NW", "SW" }; + public static CopyState RotateX(CopyState state, int angle, BlockDefinition[] defs) { CopyState newState = Clone(state); newState.Height = angle == 180 ? state.Height : state.Length; newState.Length = angle == 180 ? state.Length : state.Height; + ExtBlock[] transform; + if (angle == 90 || angle == 270) transform = Transform(defs, rotX_90_270, null); + else if (angle == 180) transform = Transform(defs, rotX_180, null); + else transform = Transform(defs, null, null); + int[] m = new int[] { posX, negZ, posY }; if (angle == 180) { m[1] = negY; m[2] = negZ; } if (angle == 270) { m[1] = posZ; m[2] = negY; } - return Rotate(state, newState, m); + return Rotate(state, newState, m, transform); } - - public static CopyState RotateY(CopyState state, int angle) { + + static string[] rotY_90 = new string[] { "N", "E", "S", "W", "NE", "SE", "SW", "NW", "WE", "NS", "WE", "NS" }; + static string[] rotY_180 = new string[] { "W", "E", "N", "S", "NS", "WE", "NE", "SW", "NW", "SE" }; + static string[] rotY_270 = new string[] { "N", "W", "S", "E", "NE", "NW", "SW", "SE", "WE", "NS", "WE", "NS" }; + public static CopyState RotateY(CopyState state, int angle, BlockDefinition[] defs) { CopyState newState = Clone(state); newState.Width = angle == 180 ? state.Width : state.Length; newState.Length = angle == 180 ? state.Length : state.Width; + + ExtBlock[] transform; + if (angle == 90) transform = Transform(defs, null, rotY_90); + else if (angle == 180) transform = Transform(defs, rotY_180, null); + else if (angle == 270) transform = Transform(defs, null, rotY_270); + else transform = Transform(defs, null, null); int[] m = new int[] { negZ, posY, posX }; if (angle == 180) { m[0] = negX; m[2] = negZ; } if (angle == 270) { m[0] = posZ; m[2] = negX; } - return Rotate(state, newState, m); + return Rotate(state, newState, m, transform); } - public static CopyState RotateZ(CopyState state, int angle) { + static string[] rotZ_90_270 = new string[] { "WE", "UD" }; + static string[] rotZ_180 = new string[] { "W", "E", "NW", "NE", "SW", "SE" }; + public static CopyState RotateZ(CopyState state, int angle, BlockDefinition[] defs) { CopyState newState = Clone(state); newState.Width = angle == 180 ? state.Width : state.Height; newState.Height = angle == 180 ? state.Height : state.Width; + ExtBlock[] transform; + if (angle == 90 || angle == 270) transform = Transform(defs, rotZ_90_270, null); + else if (angle == 180) transform = Transform(defs, rotZ_180,null); + else transform = Transform(defs, null, null); + int[] m = new int[] { posY, negX, posZ }; if (angle == 180) { m[0] = negX; m[1] = negY; } if (angle == 270) { m[0] = negY; m[1] = posX; } - return Rotate(state, newState, m); + return Rotate(state, newState, m, transform); } - static CopyState Rotate(CopyState state, CopyState flipped, int[] m) { + static CopyState Rotate(CopyState state, CopyState flipped, int[] m, ExtBlock[] transform) { int volume = state.Volume; for (int i = 0; i < volume; i++) { ushort x, y, z; state.GetCoords(i, out x, out y, out z); - ExtBlock block = state.Get(i); + ExtBlock block = transform[state.Get(i).Index]; flipped.Set(block, Rotate(m[0], x, y, z, state), @@ -101,19 +124,24 @@ namespace MCGalaxy.Drawing { return newState; } - - public static void MirrorX(CopyState state) { - int midZ = state.Length / 2, maxZ = state.Length - 1; + static string[] mirrorX = new string[] { "N", "S", "NW", "SW", "NE", "SE" }; + public static void MirrorX(CopyState state, BlockDefinition[] defs) { + // ceiling division by 2, because for odd length, we still want to + // mirror the middle row to rotate directional blocks + int midZ = (state.Length + 1) / 2, maxZ = state.Length - 1; state.OriginZ = state.OppositeOriginZ; state.Offset.Z = -state.Offset.Z; + ExtBlock[] transform = Transform(defs, mirrorX, null); for (int y = 0; y < state.Height; y++) { for (int z = 0; z < midZ; z++) { int endZ = maxZ - z; int start = state.GetIndex(0, y, z); int end = state.GetIndex(0, y, endZ); + for (int x = 0; x < state.Width; x++) { - ExtBlock blockA = state.Get(start), blockB = state.Get(end); + ExtBlock blockA = transform[state.Get(start).Index]; + ExtBlock blockB = transform[state.Get(end).Index]; state.Set(blockB, start); state.Set(blockA, end); start++; end++; } @@ -121,18 +149,22 @@ namespace MCGalaxy.Drawing { } } - public static void MirrorY(CopyState state) { - int midY = state.Height / 2, maxY = state.Height - 1; + static string[] mirrorY = new string[] { "D", "U" }; + public static void MirrorY(CopyState state, BlockDefinition[] defs) { + int midY = (state.Height + 1) / 2, maxY = state.Height - 1; state.OriginY = state.OppositeOriginY; state.Offset.Y = -state.Offset.Y; + ExtBlock[] transform = Transform(defs, mirrorY, null); for (int y = 0; y < midY; y++) { int endY = maxY - y; int start = state.GetIndex(0, y, 0); int end = state.GetIndex(0, endY, 0); + for (int z = 0; z < state.Length; z++) { for (int x = 0; x < state.Width; x++) { - ExtBlock blockA = state.Get(start), blockB = state.Get(end); + ExtBlock blockA = transform[state.Get(start).Index]; + ExtBlock blockB = transform[state.Get(end).Index]; state.Set(blockB, start); state.Set(blockA, end); start++; end++; } @@ -140,10 +172,12 @@ namespace MCGalaxy.Drawing { } } - public static void MirrorZ(CopyState state) { - int midX = state.Width / 2, maxX = state.Width - 1; + static string[] mirrorZ = new string[] { "W", "E", "NW", "NE", "SW", "SE" }; + public static void MirrorZ(CopyState state, BlockDefinition[] defs) { + int midX = (state.Width + 1) / 2, maxX = state.Width - 1; state.OriginX = state.OppositeOriginX; state.Offset.X = -state.Offset.X; + ExtBlock[] transform = Transform(defs, mirrorZ, null); for (int y = 0; y < state.Height; y++) { for (int z = 0; z < state.Length; z++) { @@ -152,11 +186,76 @@ namespace MCGalaxy.Drawing { int start = state.GetIndex(x, y, z); int end = state.GetIndex(endX, y, z); - ExtBlock blockA = state.Get(start), blockB = state.Get(end); + ExtBlock blockA = transform[state.Get(start).Index]; + ExtBlock blockB = transform[state.Get(end).Index]; state.Set(blockB, start); state.Set(blockA, end); } } } } + + + static ExtBlock[] Transform(BlockDefinition[] defs, string[] mirrorDirs, string[] rotateDirs) { + ExtBlock[] transform = new ExtBlock[Block.Count * 2]; + for (int i = 0; i < transform.Length; i++) { + transform[i] = ExtBlock.FromIndex(i); + } + if (mirrorDirs == null && rotateDirs == null) return transform; + + // Rotate/Mirror directional blocks + for (int i = 0; i < defs.Length; i++) { + if (defs[i] == null) continue; + int dirIndex = defs[i].Name.LastIndexOf('-'); + if (dirIndex == -1) continue; + + BlockDefinition transformed = null; + if (mirrorDirs != null) { + transformed = MirrorTransform(defs, i, dirIndex, mirrorDirs); + } else { + transformed = RotateTransform(defs, i, dirIndex, rotateDirs); + } + + if (transformed == null) continue; + ExtBlock src = ExtBlock.FromRaw(defs[i].BlockID); + transform[src.Index] = ExtBlock.FromRaw(transformed.BlockID); + } + return transform; + } + + static BlockDefinition MirrorTransform(BlockDefinition[] defs, int i, int dirIndex, string[] mirrorDirs) { + string dir = defs[i].Name.Substring(dirIndex + 1); + for (int j = 0; j < mirrorDirs.Length; j++) { + if (!mirrorDirs[j].CaselessEq(dir)) continue; + + // Find the mirrored directional block opposite to this one + string name = defs[i].Name.Substring(0, dirIndex); + int mirrorIdx = (j & 1) == 0 ? 1 : -1; + string mirrorDir = mirrorDirs[j + mirrorIdx]; + return Find(defs, name + "-" + mirrorDir); + } + return null; + } + + static BlockDefinition RotateTransform(BlockDefinition[] defs, int i, int dirIndex, string[] rotateDirs) { + string dir = defs[i].Name.Substring(dirIndex + 1); + for (int j = 0; j < rotateDirs.Length; j++) { + if (!rotateDirs[j].CaselessEq(dir)) continue; + + // Find the next directional block to this one in sequence + // Each sequence is a group of 4 directional blocks + string name = defs[i].Name.Substring(0, dirIndex); + int sequence = (j / 4) * 4; + string rotateDir = rotateDirs[sequence + ((j + 1) % 4)]; + return Find(defs, name + "-" + rotateDir); + } + return null; + } + + static BlockDefinition Find(BlockDefinition[] defs, string name) { + for (int i = 0; i < defs.Length; i++) { + if (defs[i] != null && defs[i].Name.CaselessEq(name)) return defs[i]; + } + return null; + } } }