using System; using System.Collections.Generic; using UnityEngine; using Range = EscapeRoomEngine.Engine.Runtime.Utilities.Range; namespace EscapeRoomEngine.Engine.Runtime { public struct Placement { /// /// The placement position is always considered to be at (0.5, 0.5) relative to the placement. /// /// In a placement of size (1, 1), the position is at the center of the placement. In a larger placement, the position is in the center of the bottom left square. /// public Vector3Int position; public Vector2Int size; public Orientation orientation; public Vector3Int BottomLeft { get { var sizeMinusOne = size - Vector2Int.one; return orientation switch { Orientation.North => position, Orientation.East => position - new Vector3Int(0, 0, sizeMinusOne.x), Orientation.South => position - new Vector3Int(sizeMinusOne.x, 0, sizeMinusOne.y), Orientation.West => position - new Vector3Int(sizeMinusOne.y, 0, 0), _ => throw new ArgumentOutOfRangeException() }; } } /// /// Get the positions of the two back corners. The first one is just the position of the placement. /// /// public (Vector3Int, Vector3Int) BackCorners { get { var width = size.x - 1; var horizontal = new Vector3Int(width, 0, 0); var vertical = new Vector3Int(0, 0, width); var otherCorner = orientation switch { Orientation.North => position + horizontal, Orientation.East => position - vertical, Orientation.South => position - horizontal, Orientation.West => position + vertical, _ => throw new ArgumentOutOfRangeException() }; return (position, otherCorner); } } /// /// Create a set with every possible combination of position and orientation of placements that fit in a given space. /// The size of the placements are given by this placement. /// /// The generated placements will fit inside the placement of the given space. public List EveryPlacementInSpace(Space space) { var placements = new List(); var sizeMinusOne = size - Vector2Int.one; var spaceSize = space.rrPlacement.size; foreach (var o in OrientationExtensions.EveryOrientation()) { Range x; Range z; switch (o) { case Orientation.North: x = new Range(0, spaceSize.x - sizeMinusOne.x); z = new Range(0, spaceSize.y - sizeMinusOne.y); break; case Orientation.East: x = new Range(0, spaceSize.x - sizeMinusOne.y); z = new Range(sizeMinusOne.x, spaceSize.y); break; case Orientation.South: x = new Range(sizeMinusOne.x, spaceSize.x); z = new Range(sizeMinusOne.y, spaceSize.y); break; case Orientation.West: x = new Range(sizeMinusOne.y, spaceSize.x); z = new Range(0, spaceSize.y - sizeMinusOne.x); break; default: throw new ArgumentOutOfRangeException(); } placements.AddRange(ConstrainedPlacements(o, size, x, z)); } return placements; } private static IEnumerable ConstrainedPlacements( Orientation orientation, Vector2Int size, Range xRange, Range zRange) { var placements = new List(xRange.Length * zRange.Length); for (var z = zRange.min; z < zRange.max; z++) { for (var x = xRange.min; x < xRange.max; x++) { placements.Add(new Placement { position = new Vector3Int(x, 0, z), size = size, orientation = orientation }); } } return placements; } /// /// Performs the specified action for each position inside the size of this placement. /// /// For the placement {(2, 0, 3), (1, 2), East} the action would be called twice with the parameters (2, 0, 3) and (3, 0, 3). /// public void ForEachPosition(Action action) { var bottomLeft = BottomLeft; for (var z = 0; z < (orientation is Orientation.North or Orientation.South ? size.y : size.x); z++) { for (var x = 0; x < (orientation is Orientation.North or Orientation.South ? size.x : size.y); x++) { action(bottomLeft + new Vector3Int(x, 0, z)); } } } public string ToStringShort() => $"{{{position}, {orientation}}}"; public override string ToString() => $"{{{position}, {size}, {orientation}}}"; } }