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 class Module { public readonly List relatedModules = new(); public ModuleState State { get; private set; } internal Vector2Int Size => srPlacement.size; internal Orientation Orientation => srPlacement.orientation; /// /// Get the space relative (SR) position of this module. /// internal Vector3Int SrPosition => srPlacement.position; /// /// Get the room relative (RR) position of this module. /// internal Vector3Int RrPosition => space?.ToRoomRelative(SrPosition) ?? SrPosition; internal readonly ModuleDescription description; /// /// The space relative (SR) placement of this module. /// public Placement srPlacement; protected readonly Space space; internal Module(Space space, ModuleDescription description) { this.space = space; this.description = description; srPlacement.size = description.modulePrefab.size; } internal bool IsType(ModuleType type) { return description.types.Contains(type); } internal bool CheckRequirements() { return PreconditionRequirement.CheckPreconditions(this, space) && PlacementRequirement.TryPlacing(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. /// The orientation of this module. /// If the position is not inside the space dimensions. internal void PlaceRoomRelative(Vector3Int rrPosition, Orientation orientation) => Place(new Placement { position = space.ToSpaceRelative(rrPosition), size = srPlacement.size, orientation = orientation }); /// /// Place this module with a position relative to the space it is in. /// /// The space relative (SR) placement of this module. Must be inside the space dimensions. /// If the position is not inside the space dimensions. internal void Place(Placement placement) { if (space != null && !placement.position.IsInsideRelative(space.rrPlacement)) { throw new EngineException($"Trying to place {this} at {placement.position} which is outside space dimensions {space.rrPlacement}."); } srPlacement = placement; Logger.Log($"{this} has been placed at {placement.position} (SR)", LogType.ModulePlacement); } /// /// Convert a position relative to this module to one relative to its space. /// The module relative position (0, 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 Vector3Int ToSpaceRelative(Vector3Int mrPosition) { // ReSharper disable once ArrangeRedundantParentheses return srPlacement.position + (Orientation switch { Orientation.North => mrPosition, Orientation.East => new Vector3Int(mrPosition.z, 0, -mrPosition.x), Orientation.South => -mrPosition, Orientation.West => new Vector3Int(-mrPosition.z, 0, mrPosition.x), _ => throw new ArgumentOutOfRangeException() }); } internal virtual void InstantiateModule(Transform parent) { Logger.Log($"Instantiating {this}", LogType.ModuleInstantiation); State = Object.Instantiate(description.modulePrefab, parent, false); State.transform.localPosition = new Vector3(SrPosition.x + .5f, 0, SrPosition.z + .5f); State.transform.Rotate(Vector3.up, Orientation.Angle()); 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()))})"; } } }