rework dimensions and orientation into Placement, optimise requirements to work on previous candidates, use vec3 for positions

This commit is contained in:
2022-11-24 11:34:11 +01:00
parent f13ba4cd95
commit 3e51410ade
36 changed files with 268 additions and 275 deletions

View File

@@ -13,7 +13,7 @@ MonoBehaviour:
m_Name: Desert m_Name: Desert
m_EditorClassIdentifier: m_EditorClassIdentifier:
minRoomSize: {x: 3, y: 3} minRoomSize: {x: 3, y: 3}
playSpace: {x: 4, y: 6} playSpace: {x: 3, y: 5}
spaceTile: {fileID: 3229991053255736984, guid: b8f192f7cebe686468af6b1a71c4605b, spaceTile: {fileID: 3229991053255736984, guid: b8f192f7cebe686468af6b1a71c4605b,
type: 3} type: 3}
environment: {fileID: 5743657079028767629, guid: 17ecdbaca50efaa4ab503614dfec54a8, environment: {fileID: 5743657079028767629, guid: 17ecdbaca50efaa4ab503614dfec54a8,

View File

@@ -19,5 +19,4 @@ MonoBehaviour:
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}

View File

@@ -20,5 +20,4 @@ MonoBehaviour:
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}

View File

@@ -19,5 +19,4 @@ MonoBehaviour:
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}

View File

@@ -20,5 +20,4 @@ MonoBehaviour:
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}

View File

@@ -15,10 +15,10 @@ MonoBehaviour:
types: 01000000 types: 01000000
modulePrefab: {fileID: 7146915386488129308, guid: d877ee36ba6ace440aebce2c20cf70d6, modulePrefab: {fileID: 7146915386488129308, guid: d877ee36ba6ace440aebce2c20cf70d6,
type: 3} type: 3}
preconditionRequirements: []
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}
connectedDoorDescription: {fileID: 11400000, guid: a70cb93176094309a847f1928d6b950d, connectedDoorDescription: {fileID: 11400000, guid: a70cb93176094309a847f1928d6b950d,
type: 2} type: 2}

View File

@@ -15,6 +15,6 @@ MonoBehaviour:
types: types:
modulePrefab: {fileID: 4604142456167599783, guid: ad2655de8289afa40aa520f9fc474681, modulePrefab: {fileID: 4604142456167599783, guid: ad2655de8289afa40aa520f9fc474681,
type: 3} type: 3}
preconditionRequirements: []
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
orientationRequirements: []

View File

@@ -15,8 +15,8 @@ MonoBehaviour:
types: 02000000 types: 02000000
modulePrefab: {fileID: 9077423192650498975, guid: e419cb35bd744b24ea973860d8b1405d, modulePrefab: {fileID: 9077423192650498975, guid: e419cb35bd744b24ea973860d8b1405d,
type: 3} type: 3}
preconditionRequirements: []
placementRequirements: placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2} - {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2} - {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}
orientationRequirements:
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2} - {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}

View File

@@ -1,55 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace EscapeRoomEngine.Engine.Runtime
{
public struct Dimensions
{
public int width;
public int length;
public int x;
public int z;
public Vector2Int Position
{
get => new(x, z);
set
{
x = value.x;
z = value.y;
}
}
public Vector2Int Size
{
get => new(width, length);
set
{
width = value.x;
length = value.y;
}
}
public int Area => width * length;
public HashSet<Vector2Int> EveryPosition
{
get
{
var positions = new HashSet<Vector2Int>();
for (var zIter = 0; zIter < length; zIter++)
{
for (var xIter = 0; xIter < width; xIter++)
{
positions.Add(new Vector2Int(xIter, zIter));
}
}
return positions;
}
}
public override string ToString() => $"({width}, {length}) at ({x}, {z})";
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b70ad3679d5c48768c1d132f56dba4dd
timeCreated: 1667222427

View File

@@ -56,7 +56,7 @@ namespace EscapeRoomEngine.Engine.Runtime
Logger.Log("Generating room...", LogType.RoomGeneration); Logger.Log("Generating room...", LogType.RoomGeneration);
// get the last entrance from the newest room or create a spawn passage with no entrance door for where the player will start // get the last entrance from the newest room or create a spawn passage with no entrance door for where the player will start
var entrance = NumberOfRooms > 0 ? _rooms.Last().exit : new Passage(new DoorModule(null, theme.spawnDoor), true); var entrance = NumberOfRooms > 0 ? _rooms.Last().exit : new Passage(new DoorModule(null, theme.spawnDoor));
var room = new Room(entrance); var room = new Room(entrance);
_rooms.Add(room); _rooms.Add(room);

View File

@@ -45,7 +45,7 @@ namespace EscapeRoomEngine.Engine.Runtime
#region Puzzles #region Puzzles
[BoxGroup("Puzzles")] [MinMaxSlider(0, 10)] [BoxGroup("Puzzles")] [MinMaxSlider(0, 24)]
public Vector2Int puzzleCount; public Vector2Int puzzleCount;
[BoxGroup("Puzzles")] [BoxGroup("Puzzles")]

View File

@@ -10,37 +10,27 @@ using Object = UnityEngine.Object;
namespace EscapeRoomEngine.Engine.Runtime.Modules namespace EscapeRoomEngine.Engine.Runtime.Modules
{ {
public enum Orientation
{
North = 0, East = 90, South = 180, West = 270
}
public class Module public class Module
{ {
public static HashSet<Orientation> EveryOrientation => new(new[]
{
Orientation.North, Orientation.East, Orientation.South, Orientation.West
});
public readonly List<Module> relatedModules = new(); public readonly List<Module> relatedModules = new();
public ModuleState State { get; private set; } public ModuleState State { get; private set; }
internal Orientation Orientation => srPlacement.orientation;
/// <summary> /// <summary>
/// Get the space relative (<i>SR</i>) position of this module. /// Get the space relative (<i>SR</i>) position of this module.
/// </summary> /// </summary>
internal Vector2Int SrPosition => srDimensions.Position; internal Vector3Int SrPosition => srPlacement.position;
/// <summary> /// <summary>
/// Get the room relative (<i>RR</i>) position of this module. /// Get the room relative (<i>RR</i>) position of this module.
/// </summary> /// </summary>
internal Vector2Int RrPosition => space.ToRoomRelative(SrPosition); internal Vector3Int RrPosition => space?.ToRoomRelative(SrPosition) ?? SrPosition;
internal readonly ModuleDescription description; internal readonly ModuleDescription description;
internal Orientation orientation;
/// <summary> /// <summary>
/// The space relative (<i>SR</i>) dimensions of this module. /// The space relative (<i>SR</i>) placement of this module.
/// </summary> /// </summary>
protected Dimensions srDimensions; public Placement srPlacement;
protected readonly Space space; protected readonly Space space;
@@ -58,49 +48,55 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
internal bool CheckRequirements() internal bool CheckRequirements()
{ {
return PreconditionRequirement.CheckPreconditions(this, space) && return PreconditionRequirement.CheckPreconditions(this, space) &&
PlacementRequirement.TryPlacing(this, space) && PlacementRequirement.TryPlacing(this, space);
OrientationRequirement.TryOrienting(this, space);
} }
/// <summary> /// <summary>
/// Place this module with a position relative to the room. /// Place this module with a position relative to the room.
/// </summary> /// </summary>
/// <param name="rrPosition">The room relative (<i>RR</i>) position of this module. Must be inside the space dimensions.</param> /// <param name="rrPosition">The room relative (<i>RR</i>) position of this module. Must be inside the space dimensions.</param>
/// <param name="orientation">The orientation of this module.</param>
/// <exception cref="EngineException">If the position is not inside the space dimensions.</exception> /// <exception cref="EngineException">If the position is not inside the space dimensions.</exception>
internal void PlaceRoomRelative(Vector2Int rrPosition) => Place(space.ToSpaceRelative(rrPosition)); internal void PlaceRoomRelative(Vector3Int rrPosition, Orientation orientation) =>
Place(new Placement
{
position = space.ToSpaceRelative(rrPosition),
size = description.modulePrefab.size,
orientation = orientation
});
/// <summary> /// <summary>
/// Place this module with a position relative to the space it is in. /// Place this module with a position relative to the space it is in.
/// </summary> /// </summary>
/// <param name="srPosition">The space relative (<i>SR</i>) position of this module. Must be inside the space dimensions.</param> /// <param name="placement">The space relative (<i>SR</i>) placement of this module. Must be inside the space dimensions.</param>
/// <exception cref="EngineException">If the position is not inside the space dimensions.</exception> /// <exception cref="EngineException">If the position is not inside the space dimensions.</exception>
internal void Place(Vector2Int srPosition) { internal void Place(Placement placement) {
if (space != null && !srPosition.IsInsideRelative(space.rrDimensions)) if (space != null && !placement.position.IsInsideRelative(space.rrPlacement))
{ {
throw new EngineException($"Trying to place {this} at {srPosition}, which is outside space dimensions {space.rrDimensions}."); throw new EngineException($"Trying to place {this} at {placement.position} which is outside space dimensions {space.rrPlacement}.");
} }
srPlacement = placement;
srDimensions.Position = srPosition; Logger.Log($"{this} has been placed at {placement.position} (SR)", LogType.ModulePlacement);
Logger.Log($"{this} has been placed at {srPosition} (SR)", LogType.ModulePlacement);
} }
/// <summary> /// <summary>
/// Convert a position relative to this module to one relative to its space. /// Convert a position relative to this module to one relative to its space.
/// <example>The module relative position <c>(0, 1)</c> should always be in front of the module, wherever it faces.</example> /// <example>The module relative position <c>(0, 0, 1)</c> should always be in front of the module, wherever it faces.</example>
/// </summary> /// </summary>
/// <param name="mrPosition">The module relative (<i>MR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param> /// <param name="mrPosition">The module relative (<i>MR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param>
/// <returns></returns> internal Vector3Int ToSpaceRelative(Vector3Int mrPosition)
internal Vector2Int ToSpaceRelative(Vector2Int mrPosition)
{ {
return srDimensions.Position + orientation switch // ReSharper disable once ArrangeRedundantParentheses
return srPlacement.position + (Orientation switch
{ {
Orientation.North => mrPosition, Orientation.North => mrPosition,
Orientation.East => new Vector2Int(mrPosition.y, -mrPosition.x), Orientation.East => new Vector3Int(mrPosition.z, 0, -mrPosition.x),
Orientation.South => -mrPosition, Orientation.South => -mrPosition,
Orientation.West => new Vector2Int(-mrPosition.y, mrPosition.x), Orientation.West => new Vector3Int(-mrPosition.z, 0, mrPosition.x),
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; });
} }
internal virtual void InstantiateModule(Transform parent) internal virtual void InstantiateModule(Transform parent)
@@ -108,8 +104,8 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
Logger.Log($"Instantiating {this}", LogType.ModuleInstantiation); Logger.Log($"Instantiating {this}", LogType.ModuleInstantiation);
State = Object.Instantiate(description.modulePrefab, parent, false); State = Object.Instantiate(description.modulePrefab, parent, false);
State.transform.localPosition = new Vector3(srDimensions.x + .5f, 0, srDimensions.z + .5f); State.transform.localPosition = new Vector3(SrPosition.x + .5f, 0, SrPosition.z + .5f);
State.transform.Rotate(Vector3.up, (float)orientation); State.transform.Rotate(Vector3.up, Orientation.Angle());
State.name = ToString(); State.name = ToString();
State.SetModule(this); State.SetModule(this);
} }

View File

@@ -14,8 +14,6 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
public List<PreconditionRequirement> preconditionRequirements = new(); public List<PreconditionRequirement> preconditionRequirements = new();
[BoxGroup("Requirements")] [BoxGroup("Requirements")]
public List<PlacementRequirement> placementRequirements = new(); public List<PlacementRequirement> placementRequirements = new();
[BoxGroup("Requirements")]
public List<OrientationRequirement> orientationRequirements = new();
internal bool HasType(ModuleType type) internal bool HasType(ModuleType type)
{ {

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace EscapeRoomEngine.Engine.Runtime
{
public enum Orientation
{
North = 0, East = 90, South = 180, West = 270
}
public static class OrientationExtensions
{
public static HashSet<Orientation> EveryOrientation() => new(new[]
{
Orientation.North, Orientation.East, Orientation.South, Orientation.West
});
public static float Angle(this Orientation orientation) => (float)orientation;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 892800b15ed4409582647cfc5659b820
timeCreated: 1669115056

View File

@@ -1,5 +1,4 @@
using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Modules;
using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger; using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType; using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
@@ -7,31 +6,12 @@ namespace EscapeRoomEngine.Engine.Runtime
{ {
public class Passage public class Passage
{ {
internal DoorModule fromOut, toIn; internal readonly DoorModule fromOut;
/// <summary> internal DoorModule toIn;
/// The room relative (<i>RR</i>) position of this passage.
/// </summary>
internal Vector2Int rrPosition;
internal Passage(DoorModule from, bool spawnPassage = false) internal Passage(DoorModule from)
{ {
if (spawnPassage) fromOut = from;
{
fromOut = from;
rrPosition = Vector2Int.zero;
}
else
{
ConnectFrom(from);
}
}
internal void ConnectFrom(DoorModule door)
{
fromOut = door;
rrPosition = fromOut.RrPosition;
Logger.Log($"Connected passage from {door} at {rrPosition} (RR)", LogType.PassageConnection);
} }
internal void ConnectTo(DoorModule door) internal void ConnectTo(DoorModule door)
@@ -39,10 +19,9 @@ namespace EscapeRoomEngine.Engine.Runtime
toIn = door; toIn = door;
// to make sure the origin of the player doesn't move, the two doors must be placed in the same location in the same orientation // to make sure the origin of the player doesn't move, the two doors must be placed in the same location in the same orientation
toIn.PlaceRoomRelative(rrPosition); toIn.PlaceRoomRelative(fromOut.RrPosition, fromOut.Orientation);
toIn.orientation = fromOut.orientation;
Logger.Log($"Connected passage to {door} at {rrPosition} (RR)", LogType.PassageConnection); Logger.Log($"Connected passage from {fromOut} to {toIn} at {toIn.RrPosition} (RR)", LogType.PassageConnection);
} }
} }
} }

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace EscapeRoomEngine.Engine.Runtime
{
public struct Placement
{
public Vector3Int position;
public Vector2Int size;
public Orientation orientation;
/// <summary>
/// Create a set with every possible combination of position and orientation given the dimensions.
///
/// <remarks>
/// The size of all placements will be (1, 1).
/// </remarks>
/// </summary>
public List<Placement> EveryPlacement
{
get
{
var placements = new List<Placement>();
for (var zIter = 0; zIter < size.y; zIter++)
{
for (var xIter = 0; xIter < size.x; xIter++)
{
placements.AddRange(OrientationExtensions.EveryOrientation().Select(o => new Placement
{
position = new Vector3Int(xIter, 0, zIter),
size = Vector2Int.one,
orientation = o
}));
}
}
return placements;
}
}
public string PositionAndOrientation() => $"{{({position.x}, {position.z}), {orientation}}}";
public override string ToString() => $"{{{position}, {size}, {orientation}}}";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fd604a23d14e49aeb740a8108bd35111
timeCreated: 1669114833

View File

@@ -5,26 +5,29 @@ using UnityEngine;
namespace EscapeRoomEngine.Engine.Runtime.Requirements namespace EscapeRoomEngine.Engine.Runtime.Requirements
{ {
[CreateAssetMenu(menuName = "Requirements/Face Space Center")] [CreateAssetMenu(menuName = "Requirements/Face Space Center")]
public class FaceSpaceCenter : OrientationRequirement public class FaceSpaceCenter : PlacementRequirement
{ {
protected override IEnumerable<Orientation> GenerateCandidates(Module module, Space space) protected override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space)
{ {
var orientation = new HashSet<Orientation>(1); float width = space.rrPlacement.size.x - 1;
float width = space.rrDimensions.width; float length = space.rrPlacement.size.y - 1;
float length = space.rrDimensions.length;
var xRel = module.SrPosition.x / (width - 1);
var zRel = module.SrPosition.y / (length - 1);
if (zRel > xRel)
{
orientation.Add(zRel > 1 - xRel ? Orientation.South : Orientation.East);
}
else
{
orientation.Add(zRel > 1 - xRel ? Orientation.West : Orientation.North);
}
return orientation; candidates.RemoveAll(candidate =>
{
var xRel = candidate.position.x / width;
var zRel = candidate.position.z / length;
return candidate.orientation !=
(zRel > xRel
? zRel > 1 - xRel
? Orientation.South
: Orientation.East
: zRel > 1 - xRel
? Orientation.West
: Orientation.North);
});
return candidates;
} }
} }
} }

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Modules;
using NaughtyAttributes;
using UnityEngine; using UnityEngine;
namespace EscapeRoomEngine.Engine.Runtime.Requirements namespace EscapeRoomEngine.Engine.Runtime.Requirements
@@ -8,21 +7,11 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
[CreateAssetMenu(menuName = "Requirements/No Overlap")] [CreateAssetMenu(menuName = "Requirements/No Overlap")]
public class NoOverlap : PlacementRequirement public class NoOverlap : PlacementRequirement
{ {
[InfoBox("A module relative position will be oriented with the module (e.g. (0, 1) is always in front of the module).")] protected override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space)
[Label("Reserved Positions (Module Relative)")]
public List<Vector2Int> mrReservedPositions;
protected override IEnumerable<Vector2Int> GenerateCandidates(Module module, Space space)
{ {
var candidates = space.rrDimensions.EveryPosition; space.AllModules.ForEach(other =>
space.AllModules.ForEach(m =>
{ {
candidates.Remove(m.SrPosition); candidates.RemoveAll(candidate => candidate.position.Equals(other.SrPosition));
m.description.placementRequirements
.FindAll(r => r is NoOverlap)
.ForEach(r => ((NoOverlap)r).mrReservedPositions
.ForEach(p => candidates.Remove(m.ToSpaceRelative(p))));
}); });
return candidates; return candidates;

View File

@@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities;
namespace EscapeRoomEngine.Engine.Runtime.Requirements
{
public abstract class OrientationRequirement : Requirement<Orientation>
{
protected abstract override IEnumerable<Orientation> GenerateCandidates(Module module, Space space);
public static bool TryOrienting(Module module, Space space)
{
if (module.description.orientationRequirements.Count == 0)
{
// don't evaluate requirements if there are none
return true;
}
var orientationCandidates = Candidates(
Module.EveryOrientation,
module.description.orientationRequirements,
module, space);
Logger.Log($"orientation candidates: {string.Join(",", orientationCandidates.ToList().ConvertAll(c => c.ToString()))}", LogType.RequirementResolution);
if (orientationCandidates.Count > 0)
{
module.orientation = orientationCandidates.RandomElement();
return true;
}
// ReSharper disable once RedundantIfElseBlock
else
{
Logger.Log("Could not find suitable orientation for module", LogType.RequirementResolution);
return false;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0704ea5393394baf921f68d2dcbdfaec
timeCreated: 1667878220

View File

@@ -7,22 +7,18 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
[CreateAssetMenu(menuName = "Requirements/Place Along Space Edges")] [CreateAssetMenu(menuName = "Requirements/Place Along Space Edges")]
public class PlaceAlongSpaceEdges : PlacementRequirement public class PlaceAlongSpaceEdges : PlacementRequirement
{ {
protected override IEnumerable<Vector2Int> GenerateCandidates(Module module, Space space) protected override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space)
{ {
var edgePositions = new HashSet<Vector2Int>(); var right = space.rrPlacement.size.x - 1;
var top = space.rrPlacement.size.y - 1;
for (var x = 0; x < space.rrDimensions.width; x++) candidates.RemoveAll(candidate =>
{ {
edgePositions.Add(new Vector2Int(x, 0)); var position = candidate.position;
edgePositions.Add(new Vector2Int(x, space.rrDimensions.length - 1)); return position.x > 0 && position.x < right && position.z > 0 && position.z < top;
} });
for (var z = 0; z < space.rrDimensions.length; z++)
{
edgePositions.Add(new Vector2Int(0, z));
edgePositions.Add(new Vector2Int(space.rrDimensions.width - 1, z));
}
return edgePositions; return candidates;
} }
} }
} }

View File

@@ -7,9 +7,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
[CreateAssetMenu(menuName = "Requirements/Place Anywhere")] [CreateAssetMenu(menuName = "Requirements/Place Anywhere")]
public class PlaceAnywhere : PlacementRequirement public class PlaceAnywhere : PlacementRequirement
{ {
protected override IEnumerable<Vector2Int> GenerateCandidates(Module module, Space space) protected override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space)
{ {
return space.rrDimensions.EveryPosition; return candidates;
} }
} }
} }

View File

@@ -2,15 +2,12 @@
using System.Linq; using System.Linq;
using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities; using EscapeRoomEngine.Engine.Runtime.Utilities;
using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
namespace EscapeRoomEngine.Engine.Runtime.Requirements namespace EscapeRoomEngine.Engine.Runtime.Requirements
{ {
public abstract class PlacementRequirement : Requirement<Vector2Int> public abstract class PlacementRequirement : Requirement<Placement>
{ {
protected abstract override IEnumerable<Vector2Int> GenerateCandidates(Module module, Space space); protected abstract override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space);
public static bool TryPlacing(Module module, Space space) public static bool TryPlacing(Module module, Space space)
{ {
@@ -21,11 +18,11 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
} }
var placementCandidates = Candidates( var placementCandidates = Candidates(
space.rrDimensions.EveryPosition, space.rrPlacement.EveryPlacement,
module.description.placementRequirements, module.description.placementRequirements,
module, space); module, space);
Logger.Log($"placement candidates: {string.Join(", ", placementCandidates.ToList().ConvertAll(c => c.ToString()))}", LogType.RequirementResolution); Logger.Log($"placement candidates: {string.Join(", ", placementCandidates.ToList().ConvertAll(c => c.PositionAndOrientation()))}", LogType.RequirementResolution);
if (placementCandidates.Count > 0) if (placementCandidates.Count > 0)
{ {

View File

@@ -7,9 +7,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
{ {
public abstract class PreconditionRequirement : Requirement<bool> public abstract class PreconditionRequirement : Requirement<bool>
{ {
protected static readonly HashSet<bool> TrueSet = new(new[] { true }), FalseSet = new(new[] { false }); private static readonly List<bool> TrueSet = new(new[] { true });
protected abstract override IEnumerable<bool> GenerateCandidates(Module module, Space space); protected abstract override List<bool> FilterCandidates(List<bool> candidates, Module module, Space space);
public static bool CheckPreconditions(Module module, Space space) public static bool CheckPreconditions(Module module, Space space)
{ {

View File

@@ -12,12 +12,12 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
[Required] [Required]
public ModuleDescription relatedModule; public ModuleDescription relatedModule;
protected override IEnumerable<bool> GenerateCandidates(Module module, Space space) protected override List<bool> FilterCandidates(List<bool> candidates, Module module, Space space)
{ {
var newModule = Module.CreateModuleByType(space, relatedModule); var newModule = Module.CreateModuleByType(space, relatedModule);
module.relatedModules.Add(newModule); module.relatedModules.Add(newModule);
return new []{ space.StageModuleWithRequirements(newModule) }; return new List<bool> { space.StageModuleWithRequirements(newModule) };
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Modules;
using UnityEngine; using UnityEngine;
@@ -6,22 +7,15 @@ namespace EscapeRoomEngine.Engine.Runtime.Requirements
{ {
public abstract class Requirement<T> : ScriptableObject public abstract class Requirement<T> : ScriptableObject
{ {
protected abstract IEnumerable<T> GenerateCandidates(Module module, Space space); protected abstract List<T> FilterCandidates(List<T> candidates, Module module, Space space);
public void Restrict(HashSet<T> candidates, Module module, Space space) => protected static List<T> Candidates(
candidates.IntersectWith(GenerateCandidates(module, space)); List<T> candidates,
public static HashSet<T> Candidates(
HashSet<T> initialCandidates,
IEnumerable<Requirement<T>> requirements, IEnumerable<Requirement<T>> requirements,
Module module, Space space) Module module, Space space)
{ {
foreach (var requirement in requirements) return requirements.Aggregate(candidates,
{ (current, requirement) => requirement.FilterCandidates(current, module, space));
requirement.Restrict(initialCandidates, module, space);
}
return initialCandidates;
} }
} }
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using UnityEngine; using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger; using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType; using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
@@ -13,7 +14,7 @@ namespace EscapeRoomEngine.Engine.Runtime
/// <summary> /// <summary>
/// The room relative (<i>RR</i>) dimensions of this space. /// The room relative (<i>RR</i>) dimensions of this space.
/// </summary> /// </summary>
internal readonly Dimensions rrDimensions; internal readonly Placement rrPlacement;
internal readonly Room room; internal readonly Room room;
/// <summary> /// <summary>
@@ -42,10 +43,10 @@ namespace EscapeRoomEngine.Engine.Runtime
internal Space(Room room, Passage entrance) internal Space(Room room, Passage entrance)
{ {
this.room = room; this.room = room;
rrDimensions = GenerateSpaceDimensions( rrPlacement = GenerateSpaceDimensions(
entrance, entrance,
Engine.DefaultEngine.theme.minRoomSize, Engine.DefaultEngine.theme.minRoomSize.ProjectAtFloor(),
Engine.DefaultEngine.theme.playSpace); Engine.DefaultEngine.theme.playSpace.ProjectAtFloor());
// connect the space to its passage // connect the space to its passage
entrance.ConnectTo(new DoorModule(this, entrance.ConnectTo(new DoorModule(this,
@@ -94,20 +95,20 @@ namespace EscapeRoomEngine.Engine.Runtime
{ {
_spaceObject = new GameObject($"Space {name}"); _spaceObject = new GameObject($"Space {name}");
_spaceObject.transform.SetParent(parent, false); _spaceObject.transform.SetParent(parent, false);
_spaceObject.transform.localPosition = new Vector3(rrDimensions.x, 0, rrDimensions.z); _spaceObject.transform.localPosition = new Vector3(rrPlacement.position.x, 0, rrPlacement.position.z);
// build the space floor out of tiles // build the space floor out of tiles
_spaceTiles = new GameObject($"Space Geometry"); _spaceTiles = new GameObject($"Space Geometry");
_spaceTiles.transform.SetParent(_spaceObject.transform, false); _spaceTiles.transform.SetParent(_spaceObject.transform, false);
_spaceTiles.isStatic = true; _spaceTiles.isStatic = true;
for (var z = 0; z < rrDimensions.length; z++) for (var z = 0; z < rrPlacement.size.y; z++)
{ {
for (var x = 0; x < rrDimensions.width; x++) for (var x = 0; x < rrPlacement.size.x; x++)
{ {
var left = x == 0; var left = x == 0;
var right = x == rrDimensions.width - 1; var right = x == rrPlacement.size.x - 1;
var bottom = z == 0; var bottom = z == 0;
var top = z == rrDimensions.length - 1; var top = z == rrPlacement.size.y - 1;
TileLocation location; TileLocation location;
if (bottom) if (bottom)
@@ -131,40 +132,40 @@ namespace EscapeRoomEngine.Engine.Runtime
/// Convert a position relative to this space to one relative to the room. /// Convert a position relative to this space to one relative to the room.
/// </summary> /// </summary>
/// <param name="srPosition">The space relative (<i>SR</i>) position that should be converted to a room relative (<i>RR</i>) position.</param> /// <param name="srPosition">The space relative (<i>SR</i>) position that should be converted to a room relative (<i>RR</i>) position.</param>
internal Vector2Int ToRoomRelative(Vector2Int srPosition) => srPosition + rrDimensions.Position; internal Vector3Int ToRoomRelative(Vector3Int srPosition) => srPosition + rrPlacement.position;
/// <summary> /// <summary>
/// Convert a position relative to the room to one relative to this space. /// Convert a position relative to the room to one relative to this space.
/// </summary> /// </summary>
/// <param name="rrPosition">The room relative (<i>RR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param> /// <param name="rrPosition">The room relative (<i>RR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param>
internal Vector2Int ToSpaceRelative(Vector2Int rrPosition) => rrPosition - rrDimensions.Position; internal Vector3Int ToSpaceRelative(Vector3Int rrPosition) => rrPosition - rrPlacement.position;
/// <summary> /// <summary>
/// Generate space dimensions that fit the required size constraints and cover the position of the entrance. /// Generate space dimensions that fit the required size constraints and cover the position of the entrance.
/// </summary> /// </summary>
/// <returns>The generated room relative (<i>RR</i>) dimensions.</returns> /// <returns>A room relative (<i>RR</i>) placement with the generated dimensions. Always faces North.</returns>
private static Dimensions GenerateSpaceDimensions(Passage entrance, Vector2Int minSize, Vector2Int availableSpace) private static Placement GenerateSpaceDimensions(Passage entrance, Vector3Int minSize, Vector3Int availableSpace)
{ {
var xMin = -1; var xMin = -1;
var xMax = -1; var xMax = -1;
var zMin = -1; var zMin = -1;
var zMax = -1; var zMax = -1;
var position = entrance.rrPosition;
var door = entrance.fromOut; var door = entrance.fromOut;
var position = door.RrPosition;
// fix the side the door is facing away from // fix the side the door is facing away from
switch (door.orientation) switch (door.Orientation)
{ {
case Orientation.North: case Orientation.North:
zMin = position.y; zMin = position.z;
zMax = Utilities.Utilities.RandomInclusive(zMin + minSize.y, availableSpace.y); zMax = Utilities.Utilities.RandomInclusive(zMin + minSize.z, availableSpace.z);
break; break;
case Orientation.East: case Orientation.East:
xMin = position.x; xMin = position.x;
xMax = Utilities.Utilities.RandomInclusive(xMin + minSize.x, availableSpace.x); xMax = Utilities.Utilities.RandomInclusive(xMin + minSize.x, availableSpace.x);
break; break;
case Orientation.South: case Orientation.South:
zMax = position.y + 1; zMax = position.z + 1;
zMin = Utilities.Utilities.RandomInclusive(0, zMax - minSize.y); zMin = Utilities.Utilities.RandomInclusive(0, zMax - minSize.z);
break; break;
case Orientation.West: case Orientation.West:
xMax = position.x + 1; xMax = position.x + 1;
@@ -180,16 +181,14 @@ namespace EscapeRoomEngine.Engine.Runtime
if(xMax == -1) if(xMax == -1)
xMax = Utilities.Utilities.RandomInclusive(Math.Max(position.x + 1, xMin + minSize.x), availableSpace.x); xMax = Utilities.Utilities.RandomInclusive(Math.Max(position.x + 1, xMin + minSize.x), availableSpace.x);
if(zMin == -1) if(zMin == -1)
zMin = Utilities.Utilities.RandomInclusive(0, Math.Min(position.y, availableSpace.y - minSize.y)); zMin = Utilities.Utilities.RandomInclusive(0, Math.Min(position.z, availableSpace.z - minSize.z));
if(zMax == -1) if(zMax == -1)
zMax = Utilities.Utilities.RandomInclusive(Math.Max(position.y + 1, zMin + minSize.y), availableSpace.y); zMax = Utilities.Utilities.RandomInclusive(Math.Max(position.z + 1, zMin + minSize.z), availableSpace.z);
var dimensions = new Dimensions var dimensions = new Placement
{ {
width = xMax - xMin, position = new Vector3Int(xMin, 0, zMin),
length = zMax - zMin, size = new Vector2Int(xMax - xMin, zMax - zMin)
x = xMin,
z = zMin
}; };
Logger.Log($"Generated space dimensions {dimensions} from entrance position {position}", LogType.RoomGeneration); Logger.Log($"Generated space dimensions {dimensions} from entrance position {position}", LogType.RoomGeneration);

View File

@@ -9,13 +9,13 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
#region Math #region Math
/// <summary> /// <summary>
/// Returns whether a position relative to some dimensions is inside those dimensions. /// Returns whether a position relative to some placement is inside its dimensions.
/// </summary> /// </summary>
/// <param name="position">The position to check, relative to the dimensions.</param> /// <param name="position">The position to check, relative to the placement.</param>
/// <param name="dimensions">The dimensions to check the position against.</param> /// <param name="placement">The placement to check the position against.</param>
public static bool IsInsideRelative(this Vector2Int position, Dimensions dimensions) public static bool IsInsideRelative(this Vector3Int position, Placement placement)
{ {
return position.x >= 0 && position.y >= 0 && position.x < dimensions.width && position.y < dimensions.length; return position.x >= 0 && position.z >= 0 && position.x < placement.size.x && position.z < placement.size.y;
} }
#endregion #endregion
@@ -29,4 +29,12 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
#endregion #endregion
} }
public static class Vector2IntExtensions
{
public static Vector3Int ProjectAtFloor(this Vector2Int vector) => vector.ProjectAtHeight(0);
public static Vector3Int ProjectAtHeight(this Vector2Int vector, int height) =>
new Vector3Int(vector.x, height, vector.y);
}
} }

View File

@@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f928b97941e3469a9015316bb5ac1309, type: 3}
m_Name: Test Puzzle
m_EditorClassIdentifier:
types: 02000000
modulePrefab: {fileID: 9077423192650498975, guid: e419cb35bd744b24ea973860d8b1405d,
type: 3}
preconditionRequirements: []
placementRequirements:
- {fileID: 11400000, guid: 43eb2a566a244964aa3a3319eaafe1a8, type: 2}
- {fileID: 11400000, guid: 1f1825b71bae09c438a1cb52603347d6, type: 2}
- {fileID: 11400000, guid: ed4830127e9381245a6af07e42c52422, type: 2}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 23cf2b6a956b6a745a88334950f088dd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 28d04249c1c4438da94b524e7d4afff2, type: 3}
m_Name: Test Theme
m_EditorClassIdentifier:
minRoomSize: {x: 4, y: 6}
playSpace: {x: 4, y: 6}
spaceTile: {fileID: 3229991053255736984, guid: b8f192f7cebe686468af6b1a71c4605b,
type: 3}
environment: {fileID: 5743657079028767629, guid: 17ecdbaca50efaa4ab503614dfec54a8,
type: 3}
puzzleColor: {r: 8, g: 3.5137255, b: 0, a: 1}
solvedColor: {r: 0.53333336, g: 7.5607843, b: 0, a: 1}
activeColor: {r: 0, g: 4.329412, b: 8, a: 1}
spawnDoor: {fileID: 11400000, guid: 6e937b2e9f774999b5962c4b40947165, type: 2}
exitDoorTypes:
- {fileID: 11400000, guid: 29e2ae36585f4e65966bc9ea2f95ac4a, type: 2}
puzzleCount: {x: 24, y: 24}
puzzleTypes:
- {fileID: 11400000, guid: 23cf2b6a956b6a745a88334950f088dd, type: 2}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac11abaacb19cac4f9f43fc3d82883fe
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1649,7 +1649,7 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
loggingEnabled: 1 loggingEnabled: 1
typeFilter: 000000000500000006000000 typeFilter: 0000000003000000050000000400000001000000
--- !u!1 &1718957584 --- !u!1 &1718957584
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0