diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorTilingPathBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorTilingPathBrush.cs index 841d4f92b1..908d6b99f7 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorTilingPathBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorTilingPathBrush.cs @@ -112,10 +112,10 @@ namespace OpenRA.Mods.Common.Widgets .FirstOrDefault(0); var isStartDirector = plan.AutoStart != Direction.None - && cpos == plan.FirstPoint - Direction.ToCVec(plan.AutoStart); + && cpos == plan.FirstPoint - plan.AutoStart.ToCVec(); var isEndDirector = plan.AutoEnd != Direction.None - && cpos == plan.LastPoint + Direction.ToCVec(plan.AutoEnd); + && cpos == plan.LastPoint + plan.AutoEnd.ToCVec(); return (isInside, isRally, rallyIndex, isStartDirector, isEndDirector); } @@ -131,7 +131,7 @@ namespace OpenRA.Mods.Common.Widgets var offset = plan.FirstPoint - to; var direction = offset != CVec.Zero - ? Direction.FromCVecRounding(offset) + ? DirectionExts.ClosestFromCVec(offset) : Direction.None; UpdatePlan(plan.WithStart(direction), true); } @@ -140,7 +140,7 @@ namespace OpenRA.Mods.Common.Widgets var offset = to - plan.LastPoint; var direction = offset != CVec.Zero - ? Direction.FromCVecRounding(offset) + ? DirectionExts.ClosestFromCVec(offset) : Direction.None; UpdatePlan(plan.WithEnd(direction), true); } @@ -248,7 +248,7 @@ namespace OpenRA.Mods.Common.Widgets if (plan.AutoEnd != Direction.None) yield return new CircleAnnotationRenderable( - CornerOfCell(plan.LastPoint) + Direction.ToWVec(plan.AutoEnd) * 768, + CornerOfCell(plan.LastPoint) + plan.AutoEnd.ToWVec() * 768, new WDist(256), 2, plan.End != Direction.None ? Color.Magenta : Color.Gray, @@ -256,7 +256,7 @@ namespace OpenRA.Mods.Common.Widgets if (plan.AutoStart != Direction.None) yield return new CircleAnnotationRenderable( - CornerOfCell(plan.FirstPoint) - Direction.ToWVec(plan.AutoStart) * 768, + CornerOfCell(plan.FirstPoint) - plan.AutoStart.ToWVec() * 768, new WDist(256), 2, plan.Start != Direction.None ? Color.Magenta : Color.Gray, diff --git a/OpenRA.Mods.Common/MapGenerator/Direction.cs b/OpenRA.Mods.Common/MapGenerator/Direction.cs index 5da45e5975..a3d9203db6 100644 --- a/OpenRA.Mods.Common/MapGenerator/Direction.cs +++ b/OpenRA.Mods.Common/MapGenerator/Direction.cs @@ -19,71 +19,80 @@ namespace OpenRA.Mods.Common.MapGenerator /// Utilities for simple directions and adjacency. Note that coordinate systems might not agree /// as to which directions are conceptually left/right or up/down. /// - public static class Direction + public enum Direction { /// No direction. - public const int None = -1; + None = -1, /// +X ("right"). - public const int R = 0; + R = 0, /// +X+Y ("right down"). - public const int RD = 1; + RD = 1, /// +Y ("down"). - public const int D = 2; + D = 2, /// -X+Y ("left down"). - public const int LD = 3; + LD = 3, /// -X ("left"). - public const int L = 4; + L = 4, /// -X-Y ("left up"). - public const int LU = 5; + LU = 5, /// -Y ("up"). - public const int U = 6; + U = 6, /// +X-Y ("right up"). - public const int RU = 7; + RU = 7, + } + + [Flags] + public enum DirectionMask + { + None = 0, /// Bitmask right. - public const int MR = 1 << R; + MR = 1 << Direction.R, /// Bitmask right-down. - public const int MRD = 1 << RD; + MRD = 1 << Direction.RD, /// Bitmask down. - public const int MD = 1 << D; + MD = 1 << Direction.D, /// Bitmask left-down. - public const int MLD = 1 << LD; + MLD = 1 << Direction.LD, /// Bitmask left. - public const int ML = 1 << L; + ML = 1 << Direction.L, /// Bitmask left-up. - public const int MLU = 1 << LU; + MLU = 1 << Direction.LU, /// Bitmask up. - public const int MU = 1 << U; + MU = 1 << Direction.U, /// Bitmask right-up. - public const int MRU = 1 << RU; + MRU = 1 << Direction.RU, + } + public static class DirectionExts + { /// Adjacent offsets with directions, excluding diagonals. - public static readonly ImmutableArray<(int2, int)> Spread4D = + public static readonly ImmutableArray<(int2, Direction)> Spread4D = [ - (new int2(1, 0), R), - (new int2(0, 1), D), - (new int2(-1, 0), L), - (new int2(0, -1), U) + (new int2(1, 0), Direction.R), + (new int2(0, 1), Direction.D), + (new int2(-1, 0), Direction.L), + (new int2(0, -1), Direction.U) ]; /// Adjacent offsets, excluding diagonals. public static readonly ImmutableArray Spread4 = - Spread4D.Select(((int2 XY, int _) v) => v.XY).ToImmutableArray(); + Spread4D.Select(((int2 XY, Direction _) v) => v.XY).ToImmutableArray(); /// /// Adjacent offsets, excluding diagonals. Assumes that CVec(1, 0) @@ -93,21 +102,21 @@ namespace OpenRA.Mods.Common.MapGenerator Spread4.Select(xy => new CVec(xy.X, xy.Y)).ToImmutableArray(); /// Adjacent offsets with directions, including diagonals. - public static readonly ImmutableArray<(int2, int)> Spread8D = + public static readonly ImmutableArray<(int2, Direction)> Spread8D = [ - (new int2(1, 0), R), - (new int2(1, 1), RD), - (new int2(0, 1), D), - (new int2(-1, 1), LD), - (new int2(-1, 0), L), - (new int2(-1, -1), LU), - (new int2(0, -1), U), - (new int2(1, -1), RU) + (new int2(1, 0), Direction.R), + (new int2(1, 1), Direction.RD), + (new int2(0, 1), Direction.D), + (new int2(-1, 1), Direction.LD), + (new int2(-1, 0), Direction.L), + (new int2(-1, -1), Direction.LU), + (new int2(0, -1), Direction.U), + (new int2(1, -1), Direction.RU) ]; /// Adjacent offsets, including diagonals. public static readonly ImmutableArray Spread8 = - Spread8D.Select(((int2 XY, int _) v) => v.XY).ToImmutableArray(); + Spread8D.Select(((int2 XY, Direction _) v) => v.XY).ToImmutableArray(); /// /// Adjacent offsets, including diagonals. Assumes that CVec(1, 0) @@ -117,34 +126,22 @@ namespace OpenRA.Mods.Common.MapGenerator Spread8.Select(xy => new CVec(xy.X, xy.Y)).ToImmutableArray(); /// Convert a non-none direction to an int2 offset. - public static int2 ToInt2(int d) + public static int2 ToInt2(this Direction direction) { - if (d >= 0 && d < 8) - return Spread8[d]; + if (direction >= Direction.R && direction <= Direction.RU) + return Spread8[(int)direction]; else throw new ArgumentException("bad direction"); } - static readonly ImmutableArray KiloVectors = - [ - new(1024, 0), - new(724, 724), - new(0, 1024), - new(-724, 724), - new(-1024, 0), - new(-724, -724), - new(0, -1024), - new(724, -724), - ]; - /// /// Convert a non-none direction to a CVec offset. Assumes that /// CVec(1, 0) corresponds to Direction.R. /// - public static CVec ToCVec(int d) + public static CVec ToCVec(this Direction direction) { - if (d >= 0 && d < 8) - return Spread8CVec[d]; + if (direction >= Direction.R && direction <= Direction.RU) + return Spread8CVec[(int)direction]; else throw new ArgumentException("bad direction"); } @@ -153,10 +150,10 @@ namespace OpenRA.Mods.Common.MapGenerator /// Convert a non-none direction to a WVec offset. Assumes that /// WVec(1, 0, 0) corresponds to Direction.R. /// - public static WVec ToWVec(int d) + public static WVec ToWVec(this Direction direction) { - if (d >= 0 && d < 8) - return new WVec(Spread8[d].X, Spread8[d].Y, 0); + if (direction >= Direction.R && direction <= Direction.RU) + return new WVec(Spread8[(int)direction].X, Spread8[(int)direction].Y, 0); else throw new ArgumentException("bad direction"); } @@ -166,32 +163,32 @@ namespace OpenRA.Mods.Common.MapGenerator /// The direction is based purely on the signs of the inputs. /// Supplying a zero-offset will throw. /// - public static int FromOffset(int dx, int dy) + public static Direction FromOffset(int dx, int dy) { if (dx > 0) { if (dy > 0) - return RD; + return Direction.RD; else if (dy < 0) - return RU; + return Direction.RU; else - return R; + return Direction.R; } else if (dx < 0) { if (dy > 0) - return LD; + return Direction.LD; else if (dy < 0) - return LU; + return Direction.LU; else - return L; + return Direction.L; } else { if (dy > 0) - return D; + return Direction.D; else if (dy < 0) - return U; + return Direction.U; else throw new ArgumentException("Bad direction"); } @@ -202,32 +199,37 @@ namespace OpenRA.Mods.Common.MapGenerator /// The direction with the closest angle wins. Keep inputs to 1000000 or less. /// Supplying a zero-offset will throw. /// - public static int FromOffsetRounding(int dx, int dy) + public static Direction ClosestFrom(int dx, int dy) { if (dx == 0 && dy == 0) - throw new ArgumentException("Bad direction"); + throw new ArgumentException("bad direction"); - var d = new int2(dx, dy); - var direction = None; - var best = 0; - for (var i = 0; i < KiloVectors.Length; i++) + var absX = Math.Abs(dx); + var absY = Math.Abs(dy); + var min = Math.Min(absX, absY); + var max = Math.Max(absX, absY); + + // 408 / 985 is an approximation of tan(Pi / 8 radians), or tan(22.5 degrees) + if (408 * max < 985 * min) { - var score = int2.Dot(d, KiloVectors[i]); - if (score > best) - { - best = score; - direction = i; - } + // Diagonal + return FromOffset(dx, dy); + } + else + { + // Cardinal + if (absX > absY) + return FromOffsetNonDiagonal(dx, 0); + else + return FromOffsetNonDiagonal(0, dy); } - - return direction; } /// /// Convert an offset (of arbitrary non-zero magnitude) to a direction. /// Supplying a zero-offset will throw. /// - public static int FromInt2(int2 delta) + public static Direction FromInt2(int2 delta) => FromOffset(delta.X, delta.Y); /// @@ -235,7 +237,7 @@ namespace OpenRA.Mods.Common.MapGenerator /// Supplying a zero-offset will throw. Assumes that CVec(1, 0) /// corresponds to Direction.R. /// - public static int FromCVec(CVec delta) + public static Direction FromCVec(CVec delta) => FromOffset(delta.X, delta.Y); /// @@ -243,23 +245,23 @@ namespace OpenRA.Mods.Common.MapGenerator /// Supplying a zero-offset will throw. Assumes that CVec(1, 0) /// corresponds to Direction.R. /// - public static int FromCVecRounding(CVec delta) - => FromOffsetRounding(delta.X, delta.Y); + public static Direction ClosestFromCVec(CVec delta) + => ClosestFrom(delta.X, delta.Y); /// /// Convert an offset (of arbitrary non-zero magnitude) to a non-diagonal direction. /// Supplying a zero-offset will throw. /// - public static int FromOffsetNonDiagonal(int dx, int dy) + public static Direction FromOffsetNonDiagonal(int dx, int dy) { if (dx - dy > 0 && dx + dy >= 0) - return R; + return Direction.R; if (dy + dx > 0 && dy - dx >= 0) - return D; + return Direction.D; if (-dx + dy > 0 && -dx - dy >= 0) - return L; + return Direction.L; if (-dy - dx > 0 && -dy + dx >= 0) - return U; + return Direction.U; throw new ArgumentException("bad direction"); } @@ -267,7 +269,7 @@ namespace OpenRA.Mods.Common.MapGenerator /// Convert an offset (of arbitrary non-zero magnitude) to a /// non-diagonal direction. Supplying a zero-offset will throw. /// - public static int FromInt2NonDiagonal(int2 delta) + public static Direction FromInt2NonDiagonal(int2 delta) => FromOffsetNonDiagonal(delta.X, delta.Y); /// @@ -275,81 +277,53 @@ namespace OpenRA.Mods.Common.MapGenerator /// non-diagonal direction. Supplying a zero-offset will throw. Assumes /// that CVec(1, 0) corresponds to Direction.R. /// - public static int FromCVecNonDiagonal(CVec delta) + public static Direction FromCVecNonDiagonal(CVec delta) => FromOffsetNonDiagonal(delta.X, delta.Y); /// Return the opposite direction. - public static int Reverse(int direction) + public static Direction Reverse(this Direction direction) { - if (direction == None) - return None; - return direction ^ 4; + if (direction != Direction.None) + return (Direction)((int)direction ^ 4); + else + return Direction.None; } - /// Convert a direction to a short string, like "None", "R", "RD", etc. - public static string ToString(int direction) + /// Converts the direction to a mask value. + public static DirectionMask ToMask(this Direction direction) { - switch (direction) - { - case None: return "None"; - case R: return "R"; - case RD: return "RD"; - case D: return "D"; - case LD: return "LD"; - case L: return "L"; - case LU: return "LU"; - case U: return "U"; - case RU: return "RU"; - default: throw new ArgumentException("bad direction"); - } + if (direction >= Direction.R && direction <= Direction.RU) + return (DirectionMask)(1 << (int)direction); + else + return DirectionMask.None; + } + } + + public static class DirectionMaskExts + { + /// Count the number of set bits (directions) in a direction mask. + public static int Count(this DirectionMask mask) + { + return int.PopCount((int)mask); } - /// Count the number of set bits in a direction mask. - public static int Count(int dm) + /// Finds the only direction set in a direction mask or returns None. + public static Direction ToDirection(this DirectionMask mask) { - var count = 0; - for (var m = dm; m != 0; m >>= 1) - if ((m & 1) == 1) - count++; - - return count; - } - - /// Finds the only direction set in a direction mask or returns NONE. - public static int FromMask(int mask) - { - switch (mask) - { - case MR: return R; - case MRD: return RD; - case MD: return D; - case MLD: return LD; - case ML: return L; - case MLU: return LU; - case MU: return U; - case MRU: return RU; - default: return None; - } + var d = int.Log2((int)mask); + if (1 << d == (int)mask) + return (Direction)d; + else + return Direction.None; } /// True if diagonal, false if horizontal/vertical, throws otherwise. - public static bool IsDiagonal(int direction) + public static bool IsDiagonal(this Direction direction) { - switch (direction) - { - case R: - case D: - case L: - case U: - return false; - case RD: - case LD: - case LU: - case RU: - return true; - default: - throw new ArgumentException("NONE or bad direction"); - } + if (direction >= Direction.R && direction <= Direction.RU) + return ((int)direction & 1) == 1; + else + throw new ArgumentException("None or bad direction"); } } } diff --git a/OpenRA.Mods.Common/MapGenerator/MatrixUtils.cs b/OpenRA.Mods.Common/MapGenerator/MatrixUtils.cs index 14e87e99f2..e7fb300daa 100644 --- a/OpenRA.Mods.Common/MapGenerator/MatrixUtils.cs +++ b/OpenRA.Mods.Common/MapGenerator/MatrixUtils.cs @@ -189,7 +189,7 @@ namespace OpenRA.Mods.Common.MapGenerator output[xy] = distance; unprocessed[i] = int.MaxValue; - foreach (var (offset, direction) in Direction.Spread8D) + foreach (var (offset, direction) in DirectionExts.Spread8D) { var nextXY = xy + offset; if (!passable.ContainsXY(nextXY)) @@ -199,7 +199,7 @@ namespace OpenRA.Mods.Common.MapGenerator if (output[nextXY] != int.MaxValue) continue; int nextDistance; - if (Direction.IsDiagonal(direction)) + if (direction.IsDiagonal()) nextDistance = distance + Diagonal; else nextDistance = distance + Straight; @@ -250,7 +250,7 @@ namespace OpenRA.Mods.Common.MapGenerator } } - FloodFill(space.Size, [(new int2(x, y), holeCount)], Filler, Direction.Spread4); + FloodFill(space.Size, [(new int2(x, y), holeCount)], Filler, DirectionExts.Spread4); } const int UNASSIGNED = int.MaxValue; @@ -322,7 +322,7 @@ namespace OpenRA.Mods.Common.MapGenerator } } - FloodFill(size, seeds, Filler, Direction.Spread4); + FloodFill(size, seeds, Filler, DirectionExts.Spread4); } var deflatedSize = size + new int2(1, 1); @@ -346,10 +346,10 @@ namespace OpenRA.Mods.Common.MapGenerator } deflated[cx, cy] = (byte)( - (neighborhood[0] != neighborhood[1] ? Direction.MU : 0) | - (neighborhood[1] != neighborhood[3] ? Direction.MR : 0) | - (neighborhood[3] != neighborhood[2] ? Direction.MD : 0) | - (neighborhood[2] != neighborhood[0] ? Direction.ML : 0)); + (neighborhood[0] != neighborhood[1] ? DirectionMask.MU : 0) | + (neighborhood[1] != neighborhood[3] ? DirectionMask.MR : 0) | + (neighborhood[3] != neighborhood[2] ? DirectionMask.MD : 0) | + (neighborhood[2] != neighborhood[0] ? DirectionMask.ML : 0)); } return deflated; @@ -776,7 +776,7 @@ namespace OpenRA.Mods.Common.MapGenerator roominess.Size, seeds, Filler, - Direction.Spread8); + DirectionExts.Spread8); return roominess; } @@ -813,7 +813,7 @@ namespace OpenRA.Mods.Common.MapGenerator { var from = pointArray[i - 1]; var to = pointArray[i]; - var direction = Direction.FromInt2(to - from); + var direction = DirectionExts.FromInt2(to - from); var fx = from.X; var fy = from.Y; switch (direction) @@ -850,7 +850,7 @@ namespace OpenRA.Mods.Common.MapGenerator return prop; } - FloodFill(size, seeds, FillChirality, Direction.Spread4); + FloodFill(size, seeds, FillChirality, DirectionExts.Spread4); return chirality; } @@ -898,7 +898,7 @@ namespace OpenRA.Mods.Common.MapGenerator // Looping paths contain the start/end point twice. var paths = new List(); - void TracePath(int sx, int sy, int direction) + void TracePath(int sx, int sy, Direction direction) { var points = new List(); var x = sx; @@ -1214,18 +1214,18 @@ namespace OpenRA.Mods.Common.MapGenerator for (var cx = 0; cx < matrix.Size.X; cx++) { var fromPos = new int2(cx, cy); - var fromDm = matrix[fromPos]; - foreach (var (offset, d) in Direction.Spread8D) + var fromDm = (DirectionMask)matrix[fromPos]; + foreach (var (offset, d) in DirectionExts.Spread8D) { - if ((fromDm & (1 << d)) == 0) + if ((fromDm & d.ToMask()) == DirectionMask.None) continue; - var dr = Direction.Reverse(d); + var dr = d.Reverse(); var toPos = new int2(cx + offset.X, cy + offset.Y); - if (matrix.ContainsXY(toPos) && (matrix[toPos] & (1 << dr)) != 0) + if (matrix.ContainsXY(toPos) && ((DirectionMask)matrix[toPos] & dr.ToMask()) != DirectionMask.None) continue; - matrix[fromPos] = (byte)(output[fromPos] & ~(1 << d)); + matrix[fromPos] = (byte)((DirectionMask)output[fromPos] & ~d.ToMask()); } } } @@ -1236,17 +1236,17 @@ namespace OpenRA.Mods.Common.MapGenerator for (var cy = 0; cy < input.Size.Y; cy++) for (var cx = 0; cx < input.Size.X; cx++) { - var dm = input[cx, cy]; - if (Direction.Count(dm) > 2) + var dm = (DirectionMask)input[cx, cy]; + if (dm.Count() > 2) { output[cx, cy] = 0; - foreach (var (offset, d) in Direction.Spread8D) + foreach (var (offset, d) in DirectionExts.Spread8D) { var xy = new int2(cx + offset.X, cy + offset.Y); if (!input.ContainsXY(xy)) continue; - var dr = Direction.Reverse(d); - output[xy] = (byte)(output[xy] & ~(1 << dr)); + var dr = d.Reverse(); + output[xy] = (byte)((DirectionMask)output[xy] & ~dr.ToMask()); } } } @@ -1266,20 +1266,20 @@ namespace OpenRA.Mods.Common.MapGenerator // Find non-loops, starting at terminals. var pointArrays = new List(); - void TracePoints(int2 xy, int reverseDm) + void TracePoints(int2 xy, DirectionMask reverseDm) { var points = new List(); bool AddPoint() { points.Add(xy); - var dm = links[xy] & ~reverseDm; + var dm = (DirectionMask)links[xy] & ~reverseDm; links[xy] = 0; - foreach (var (offset, d) in Direction.Spread8D) - if ((dm & (1 << d)) != 0) + foreach (var (offset, d) in DirectionExts.Spread8D) + if ((dm & d.ToMask()) != DirectionMask.None) { xy += offset; - reverseDm = 1 << Direction.Reverse(d); + reverseDm = d.Reverse().ToMask(); return true; } @@ -1294,7 +1294,7 @@ namespace OpenRA.Mods.Common.MapGenerator for (var sy = 0; sy < links.Size.Y; sy++) for (var sx = 0; sx < links.Size.X; sx++) - if (Direction.FromMask(links[sx, sy]) != Direction.None) + if (((DirectionMask)links[sx, sy]).ToDirection() != Direction.None) TracePoints(new int2(sx, sy), 0); // All non-loops have been removed, leaving only loops left. @@ -1303,7 +1303,7 @@ namespace OpenRA.Mods.Common.MapGenerator if (links[sx, sy] != 0) { // Choose direction with most-significant bit - var reverseDm = links[sx, sy] & (links[sx, sy] - 1); + var reverseDm = (DirectionMask)(links[sx, sy] & (links[sx, sy] - 1)); TracePoints(new int2(sx, sy), reverseDm); } @@ -1351,8 +1351,8 @@ namespace OpenRA.Mods.Common.MapGenerator { toDelete.Add(point); if (pointArray.Length < minimumJunctionSeparation) - foreach (var (offset, d) in Direction.Spread8D) - if ((links[point] & (1 << d)) != 0) + foreach (var (offset, d) in DirectionExts.Spread8D) + if (((DirectionMask)links[point] & d.ToMask()) != DirectionMask.None) toDelete.Add(point + offset); } diff --git a/OpenRA.Mods.Common/MapGenerator/PlayableSpace.cs b/OpenRA.Mods.Common/MapGenerator/PlayableSpace.cs index 383cf219b6..0f31fd1391 100644 --- a/OpenRA.Mods.Common/MapGenerator/PlayableSpace.cs +++ b/OpenRA.Mods.Common/MapGenerator/PlayableSpace.cs @@ -123,7 +123,7 @@ namespace OpenRA.Mods.Common.MapGenerator playable, [(start, true)], Filler, - Direction.Spread4CVec); + DirectionExts.Spread4CVec); } foreach (var mpos in map.AllCells.MapCoords) diff --git a/OpenRA.Mods.Common/MapGenerator/TilingPath.cs b/OpenRA.Mods.Common/MapGenerator/TilingPath.cs index 6ac1315353..2e03c05cec 100644 --- a/OpenRA.Mods.Common/MapGenerator/TilingPath.cs +++ b/OpenRA.Mods.Common/MapGenerator/TilingPath.cs @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.MapGenerator /// Direction to use for this terminal. /// If the direction here is null, it will be determined automatically later. /// - public int? Direction; + public Direction? Direction; /// /// A string which can match the format used by MultiBrushSegment's Start or End. @@ -42,11 +42,11 @@ namespace OpenRA.Mods.Common.MapGenerator { var direction = Direction ?? throw new InvalidOperationException("Direction is null"); - return $"{Type}.{MapGenerator.Direction.ToString(direction)}"; + return $"{Type}.{direction}"; } } - public Terminal(string type, int? direction) + public Terminal(string type, Direction? direction) { Type = type; Direction = direction; @@ -249,9 +249,9 @@ namespace OpenRA.Mods.Common.MapGenerator public readonly CVec Offset; public readonly CVec Moves; public readonly CVec[] RelativePoints; - public readonly int[] Directions; - public readonly int[] DirectionMasks; - public readonly int[] ReverseDirectionMasks; + public readonly Direction[] Directions; + public readonly DirectionMask[] DirectionMasks; + public readonly DirectionMask[] ReverseDirectionMasks; public TilingSegment(MultiBrush multiBrush, int startId, int endId) { @@ -264,9 +264,9 @@ namespace OpenRA.Mods.Common.MapGenerator .Select(p => p - multiBrush.Segment.Points[0]) .ToArray(); - Directions = new int[RelativePoints.Length]; - DirectionMasks = new int[RelativePoints.Length]; - ReverseDirectionMasks = new int[RelativePoints.Length]; + Directions = new Direction[RelativePoints.Length]; + DirectionMasks = new DirectionMask[RelativePoints.Length]; + ReverseDirectionMasks = new DirectionMask[RelativePoints.Length]; // Last point has no direction. Directions[^1] = Direction.None; @@ -274,12 +274,12 @@ namespace OpenRA.Mods.Common.MapGenerator ReverseDirectionMasks[^1] = 0; for (var i = 0; i < RelativePoints.Length - 1; i++) { - var direction = Direction.FromCVec(RelativePoints[i + 1] - RelativePoints[i]); + var direction = DirectionExts.FromCVec(RelativePoints[i + 1] - RelativePoints[i]); if (direction == Direction.None) throw new ArgumentException("MultiBrushSegment has duplicate points in sequence"); Directions[i] = direction; - DirectionMasks[i] = 1 << direction; - ReverseDirectionMasks[i] = 1 << Direction.Reverse(direction); + DirectionMasks[i] = direction.ToMask(); + ReverseDirectionMasks[i] = direction.Reverse().ToMask(); } } } @@ -349,8 +349,8 @@ namespace OpenRA.Mods.Common.MapGenerator var start = Start; var end = End; - start.Direction ??= Direction.FromCVec(Points[1] - Points[0]); - end.Direction ??= Direction.FromCVec(IsLoop ? Points[1] - Points[0] : Points[^1] - Points[^2]); + start.Direction ??= DirectionExts.FromCVec(Points[1] - Points[0]); + end.Direction ??= DirectionExts.FromCVec(IsLoop ? Points[1] - Points[0] : Points[^1] - Points[^2]); var maxSkip = MaxSkip > 0 ? MaxSkip : (2 * MaxDeviation + 1); @@ -472,7 +472,7 @@ namespace OpenRA.Mods.Common.MapGenerator lows.Clear(); highs.Clear(); - foreach (var offset in Direction.Spread8) + foreach (var offset in DirectionExts.Spread8) { var neighbor = xy + offset; if (!deviations.ContainsXY(neighbor) || @@ -502,7 +502,7 @@ namespace OpenRA.Mods.Common.MapGenerator size, progressSeeds, ProgressFiller, - Direction.Spread8); + DirectionExts.Spread8); var separationSeeds = new List<(int2, int)>(); @@ -521,7 +521,7 @@ namespace OpenRA.Mods.Common.MapGenerator if (MinSeparation > 0) { - foreach (var offset in Direction.Spread8) + foreach (var offset in DirectionExts.Spread8) { var neighbor = xy + offset; if (!deviations.ContainsXY(neighbor) || @@ -553,7 +553,7 @@ namespace OpenRA.Mods.Common.MapGenerator size, separationSeeds, SeparationFiller, - Direction.Spread8); + DirectionExts.Spread8); } var pathStart = points[0]; @@ -861,7 +861,7 @@ namespace OpenRA.Mods.Common.MapGenerator fallbackDistances.Size, [(new int2(maxEndDeviation, maxEndDeviation), 0)], FallbacksFiller, - Direction.Spread4); + DirectionExts.Spread4); var bestDistance = fallbackDistances.Data.Min(); if (bestDistance == Unreached || bestDistance == Unsolved) @@ -943,17 +943,17 @@ namespace OpenRA.Mods.Common.MapGenerator if (inertialRange > points.Length - 1) inertialRange = points.Length - 1; - var sd = Direction.FromCVecNonDiagonal(points[inertialRange] - points[0]); - var ed = Direction.FromCVecNonDiagonal(points[^1] - points[^(inertialRange + 1)]); + var sd = DirectionExts.FromCVecNonDiagonal(points[inertialRange] - points[0]); + var ed = DirectionExts.FromCVecNonDiagonal(points[^1] - points[^(inertialRange + 1)]); var newPoints = new CPos[points.Length + extensionLength * 2]; for (var i = 0; i < extensionLength; i++) - newPoints[i] = points[0] - Direction.ToCVec(sd) * (extensionLength - i); + newPoints[i] = points[0] - sd.ToCVec() * (extensionLength - i); Array.Copy(points, 0, newPoints, extensionLength, points.Length); for (var i = 0; i < extensionLength; i++) - newPoints[extensionLength + points.Length + i] = points[^1] + Direction.ToCVec(ed) * (i + 1); + newPoints[extensionLength + points.Length + i] = points[^1] + ed.ToCVec() * (i + 1); return newPoints; } @@ -1341,7 +1341,7 @@ namespace OpenRA.Mods.Common.MapGenerator for (var i = 1; i < points.Length; i++) { var offset = lastPoint - points[i]; - if (Direction.ToCVec(Direction.FromCVecNonDiagonal(offset)) != offset) + if (DirectionExts.FromCVecNonDiagonal(offset).ToCVec() != offset) return false; lastPoint = points[i]; diff --git a/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs b/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs index 7a9d7550f9..325f6d7c83 100644 --- a/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs +++ b/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs @@ -1049,7 +1049,7 @@ namespace OpenRA.Mods.Common.Traits regionMask, map.AllCells.Where(cpos => regionMask[cpos] == largest.Id).Select(cpos => (cpos, true)), AdoptPartiallyPlayableIntoLargest, - Direction.Spread4CVec); + DirectionExts.Spread4CVec); if (param.DenyWalledAreas) { @@ -1086,7 +1086,7 @@ namespace OpenRA.Mods.Common.Traits map.Tiles, unplayableWater.Select(cpos => (cpos, false)), ClearWaterBody, - Direction.Spread4CVec); + DirectionExts.Spread4CVec); } var replaceable = IdentifyReplaceableTiles(map, replaceabilityMap, actorPlans); diff --git a/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs b/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs index 2d589e2f36..7f587ada51 100644 --- a/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs +++ b/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs @@ -54,11 +54,11 @@ namespace OpenRA.Mods.Common.Traits /// public sealed class PathPlan { - public readonly int Start; - public readonly int End; + public readonly Direction Start; + public readonly Direction End; public readonly bool Loop; public readonly ImmutableArray Rallies; - public int AutoStart + public Direction AutoStart { get { @@ -69,14 +69,14 @@ namespace OpenRA.Mods.Common.Traits else { if (Rallies.Length >= 2) - return Direction.FromCVecRounding(Rallies[1] - Rallies[0]); + return DirectionExts.ClosestFromCVec(Rallies[1] - Rallies[0]); else return Direction.None; } } } - public int AutoEnd + public Direction AutoEnd { get { @@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Traits else { if (Rallies.Length >= 2) - return Direction.FromCVecRounding(Rallies[^1] - Rallies[^2]); + return DirectionExts.ClosestFromCVec(Rallies[^1] - Rallies[^2]); else return Direction.None; } @@ -101,7 +101,7 @@ namespace OpenRA.Mods.Common.Traits public CPos FirstPoint => Rallies[0]; public CPos LastPoint => Loop ? Rallies[0] : Rallies[^1]; - PathPlan(int start, int end, bool loop, ImmutableArray rallies) + PathPlan(Direction start, Direction end, bool loop, ImmutableArray rallies) { if (rallies == null || rallies.Length == 0) throw new ArgumentException("rallies must have at least one point"); @@ -122,13 +122,13 @@ namespace OpenRA.Mods.Common.Traits } /// Return a copy, modifying the start direction. - public PathPlan WithStart(int start) + public PathPlan WithStart(Direction start) { return new PathPlan(start, End, Loop, Rallies); } /// Return a copy, modifying the end direction. - public PathPlan WithEnd(int end) + public PathPlan WithEnd(Direction end) { return new PathPlan(Start, end, Loop, Rallies); } @@ -191,16 +191,16 @@ namespace OpenRA.Mods.Common.Traits } return new PathPlan( - Direction.Reverse(reversedStart), - Direction.Reverse(reversedEnd), + reversedStart.Reverse(), + reversedEnd.Reverse(), Loop, Rallies.Skip(1).Append(Rallies[0]).Reverse().ToImmutableArray()); } else { return new PathPlan( - Direction.Reverse(End), - Direction.Reverse(Start), + End.Reverse(), + Start.Reverse(), Loop, Rallies.Reverse().ToImmutableArray()); } @@ -228,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits var points = new List<(CPos CPos, int RallyIndex)>(); var cpos = Rallies[0]; points.Add((cpos, 0)); - var inertia = Direction.ToCVec(AutoStart); + var inertia = AutoStart.ToCVec(); if (inertia.X != 0 && inertia.Y != 0) inertia = new CVec(inertia.X, 0); @@ -267,9 +267,9 @@ namespace OpenRA.Mods.Common.Traits inertia = new CVec(0, yStep); else inertia = - Direction.ToCVec( - Direction.FromCVecNonDiagonal( - inertia + new CVec(xStep * 2, yStep * 2))); + DirectionExts.FromCVecNonDiagonal( + inertia + new CVec(xStep * 2, yStep * 2)) + .ToCVec(); while (cpos != target) {