using System; using System.Collections.Generic; using System.Linq; using EscapeRoomEngine.Engine.Runtime.Requirements; using EscapeRoomEngine.Engine.Runtime.Utilities; using UnityEngine; using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger; using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType; using Object = UnityEngine.Object; namespace EscapeRoomEngine.Engine.Runtime.Modules { public enum Orientation { North = 0, East = 90, South = 180, West = 270 } public class Module { public static HashSet EveryOrientation => new(new[] { Orientation.North, Orientation.East, Orientation.South, Orientation.West }); public readonly List relatedModules = new(); public ModuleState State { get; private set; } /// /// Get the space relative (SR) position of this module. /// internal Vector2Int SrPosition => srDimensions.Position; /// /// Get the room relative (RR) position of this module. /// internal Vector2Int RrPosition => space.ToRoomRelative(SrPosition); internal readonly ModuleDescription description; internal Orientation orientation; /// /// The space relative (SR) dimensions of this module. /// protected Dimensions srDimensions; protected readonly Space space; internal Module(Space space, ModuleDescription description) { this.space = space; this.description = description; } internal bool IsType(ModuleType type) { return description.types.Contains(type); } internal bool CheckRequirements() { return PreconditionRequirement.CheckPreconditions(this, space) && PlacementRequirement.TryPlacing(this, space) && OrientationRequirement.TryOrienting(this, space); } /// /// Place this module with a position relative to the room. /// /// The room relative (RR) position of this module. Must be inside the space dimensions. /// If the position is not inside the space dimensions. internal void PlaceRoomRelative(Vector2Int rrPosition) => Place(space.ToSpaceRelative(rrPosition)); /// /// Place this module with a position relative to the space it is in. /// /// The space relative (SR) position of this module. Must be inside the space dimensions. /// If the position is not inside the space dimensions. internal void Place(Vector2Int srPosition) { if (space != null && !srPosition.IsInsideRelative(space.rrDimensions)) { throw new EngineException($"Trying to place {this} at {srPosition}, which is outside space dimensions {space.rrDimensions}."); } srDimensions.Position = srPosition; Logger.Log($"{this} has been placed at {srPosition} (SR)", LogType.ModulePlacement); } /// /// Convert a position relative to this module to one relative to its space. /// The module relative position (0, 1) should always be in front of the module, wherever it faces. /// /// The module relative (MR) position that should be converted to a space relative (SR) position. /// internal Vector2Int ToSpaceRelative(Vector2Int mrPosition) { return srDimensions.Position + orientation switch { Orientation.North => mrPosition, Orientation.East => new Vector2Int(mrPosition.y, -mrPosition.x), Orientation.South => -mrPosition, Orientation.West => new Vector2Int(-mrPosition.y, mrPosition.x), _ => throw new ArgumentOutOfRangeException() }; } internal virtual void InstantiateModule(Transform parent) { Logger.Log($"Instantiating {this}", LogType.RoomGeneration); State = Object.Instantiate(description.modulePrefab, parent, false); State.transform.localPosition = new Vector3(srDimensions.x + .5f, 0, srDimensions.z + .5f); State.transform.Rotate(Vector3.up, (float)orientation); State.name = ToString(); State.SetModule(this); } /// /// Creates a module of the correct type given a module description. /// /// If the module description shows "Puzzle" as a type, this will try to create a puzzle module. /// /// The space to create the module for. /// The module description to create the module from. /// A or . /// If there is no module type fitting the description. internal static Module CreateModuleByType(Space space, ModuleDescription description) { if (description.HasType(ModuleType.Puzzle) && description is PuzzleModuleDescription puzzleModuleDescription) { return new PuzzleModule(space, puzzleModuleDescription); } if((description.HasType(ModuleType.DoorEntrance) || description.HasType(ModuleType.DoorExit)) && description is DoorModuleDescription doorModuleDescription) { return new DoorModule(space, doorModuleDescription); } if (description.HasType(ModuleType.Generic)) { return new Module(space, description); } throw new WrongTypeException("There is no module type fitting this description."); } public override string ToString() { return $"Module ({string.Join(", ", description.types.ToList().ConvertAll(type => type.ToString()))})"; } } }