using System;
using System.Collections.Generic;
using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Requirements;
using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
using Object = UnityEngine.Object;
namespace EscapeRoomEngine.Engine.Runtime
{
public class Space
{
///
/// The room relative (RR) dimensions of this space.
///
internal readonly Dimensions rrDimensions;
internal List Modules { get; } = new(2);
internal readonly Room room;
private GameObject _spaceObject, _spaceTiles;
internal Space(Room room, Passage entrance)
{
this.room = room;
rrDimensions = GenerateSpaceDimensions(
entrance,
Engine.DefaultEngine.theme.minRoomSize,
Engine.DefaultEngine.theme.playSpace);
// connect the space to its passage
entrance.ConnectTo(new DoorModule(this,
((DoorModuleDescription)entrance.fromOut.description).connectedDoorDescription));
AddModule(entrance.toIn);
}
internal void AddModule(Module module)
{
Modules.Add(module);
}
internal bool AddModuleWithRequirements(Module module)
{
var requirementsFulfilled =
PreconditionRequirement.CheckPreconditions(module, this) &&
PlacementRequirement.TryPlacing(module, this) &&
OrientationRequirement.TryOrienting(module, this);
if (requirementsFulfilled)
{
AddModule(module);
}
return requirementsFulfilled;
}
internal void InstantiateSpace(Transform parent, string name)
{
_spaceObject = new GameObject($"Space {name}");
_spaceObject.transform.SetParent(parent, false);
_spaceObject.transform.localPosition = new Vector3(rrDimensions.x, 0, rrDimensions.z);
// build the space floor out of tiles
_spaceTiles = new GameObject($"Space Geometry");
_spaceTiles.transform.SetParent(_spaceObject.transform, false);
_spaceTiles.isStatic = true;
for (var z = 0; z < rrDimensions.length; z++)
{
for (var x = 0; x < rrDimensions.width; x++)
{
var left = x == 0;
var right = x == rrDimensions.width - 1;
var bottom = z == 0;
var top = z == rrDimensions.length - 1;
TileLocation location;
if (bottom)
location = left ? TileLocation.SW : right ? TileLocation.SE : TileLocation.S;
else if (top)
location = left ? TileLocation.NW : right ? TileLocation.NE : TileLocation.N;
else
location = left ? TileLocation.W : right ? TileLocation.E : TileLocation.C;
var tileObject = Object.Instantiate(Engine.DefaultEngine.theme.spaceTile, _spaceTiles.transform, false);
tileObject.transform.localPosition = new Vector3(x, 0, z);
tileObject.showTile = location;
}
}
// instantiate all modules inside this space
Modules.ForEach(module => module.InstantiateModule(_spaceObject.transform));
}
///
/// Convert a position relative to this space to one relative to the room.
///
/// The space relative (SR) position that should be converted to a room relative (RR) position.
internal Vector2Int ToRoomRelative(Vector2Int srPosition) => srPosition + rrDimensions.Position;
///
/// Convert a position relative to the room to one relative to this space.
///
/// The room relative (RR) position that should be converted to a space relative (SR) position.
internal Vector2Int ToSpaceRelative(Vector2Int rrPosition) => rrPosition - rrDimensions.Position;
///
/// Generate space dimensions that fit the required size constraints and cover the position of the entrance.
///
/// The generated room relative (RR) dimensions.
private static Dimensions GenerateSpaceDimensions(Passage entrance, Vector2Int minSize, Vector2Int availableSpace)
{
var xMin = -1;
var xMax = -1;
var zMin = -1;
var zMax = -1;
var position = entrance.rrPosition;
var door = entrance.fromOut;
// fix the side the door is facing away from
switch (door.orientation)
{
case Orientation.North:
zMin = position.y;
zMax = Utilities.Utilities.RandomInclusive(zMin + minSize.y, availableSpace.y);
break;
case Orientation.East:
xMin = position.x;
xMax = Utilities.Utilities.RandomInclusive(xMin + minSize.x, availableSpace.x);
break;
case Orientation.South:
zMax = position.y + 1;
zMin = Utilities.Utilities.RandomInclusive(0, zMax - minSize.y);
break;
case Orientation.West:
xMax = position.x + 1;
xMin = Utilities.Utilities.RandomInclusive(0, xMax - minSize.x);
break;
default:
throw new ArgumentOutOfRangeException();
}
// calculate remaining values if they haven't been covered by the switch statement yet
if(xMin == -1)
xMin = Utilities.Utilities.RandomInclusive(0, Math.Min(position.x, availableSpace.x - minSize.x));
if(xMax == -1)
xMax = Utilities.Utilities.RandomInclusive(Math.Max(position.x + 1, xMin + minSize.x), availableSpace.x);
if(zMin == -1)
zMin = Utilities.Utilities.RandomInclusive(0, Math.Min(position.y, availableSpace.y - minSize.y));
if(zMax == -1)
zMax = Utilities.Utilities.RandomInclusive(Math.Max(position.y + 1, zMin + minSize.y), availableSpace.y);
var dimensions = new Dimensions(xMax - xMin, zMax - zMin, xMin, zMin);
Logger.Log($"Generated space dimensions {dimensions} from entrance position {position}", LogType.RoomGeneration);
return dimensions;
}
}
}