comment pass
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using NaughtyAttributes;
|
using NaughtyAttributes;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||||
@@ -30,10 +31,16 @@ namespace EscapeRoomEngine.Desert.Runtime
|
|||||||
|
|
||||||
public delegate void ButtonEventHandler(Button source, ButtonEventType e);
|
public delegate void ButtonEventHandler(Button source, ButtonEventType e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A general component for buttons that handles button events.
|
||||||
|
/// </summary>
|
||||||
public class Button : MonoBehaviour
|
public class Button : MonoBehaviour
|
||||||
{
|
{
|
||||||
public event ButtonEventHandler ButtonEvent;
|
public event ButtonEventHandler ButtonEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this button accepts input.
|
||||||
|
/// </summary>
|
||||||
[ShowNativeProperty]
|
[ShowNativeProperty]
|
||||||
protected bool Active
|
protected bool Active
|
||||||
{
|
{
|
||||||
@@ -50,6 +57,9 @@ namespace EscapeRoomEngine.Desert.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this button is currently pressed.
|
||||||
|
/// </summary>
|
||||||
[ShowNativeProperty]
|
[ShowNativeProperty]
|
||||||
protected bool Pressed
|
protected bool Pressed
|
||||||
{
|
{
|
||||||
@@ -88,11 +98,15 @@ namespace EscapeRoomEngine.Desert.Runtime
|
|||||||
|
|
||||||
#region Debug Buttons
|
#region Debug Buttons
|
||||||
|
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Enable() => Active = true;
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Disable() => Active = false;
|
public void Enable() => Active = true;
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Press() => Pressed = true;
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Release() => Pressed = false;
|
public void Disable() => Active = false;
|
||||||
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
|
public void Press() => Pressed = true;
|
||||||
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
|
public void Release() => Pressed = false;
|
||||||
|
[UsedImplicitly]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
public void PressAndRelease()
|
public void PressAndRelease()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,11 +4,19 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime
|
namespace EscapeRoomEngine.Desert.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A rotating crystal that can change colour.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Emission))]
|
[RequireComponent(typeof(Emission))]
|
||||||
public class Crystal : MonoBehaviour
|
public class Crystal : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Required] public Light crystalLight;
|
[Required]
|
||||||
|
[BoxGroup("Internal")]
|
||||||
|
public Light crystalLight;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Turns the crystal light on or off.
|
||||||
|
/// </summary>
|
||||||
public bool Active
|
public bool Active
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
@@ -18,6 +26,9 @@ namespace EscapeRoomEngine.Desert.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The crystal light and emission colours.
|
||||||
|
/// </summary>
|
||||||
public DynamicColor Color
|
public DynamicColor Color
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -6,13 +6,18 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the dispenser module.
|
||||||
|
/// </summary>
|
||||||
[SelectionBase]
|
[SelectionBase]
|
||||||
[RequireComponent(typeof(Animator), typeof(Emission))]
|
[RequireComponent(typeof(Animator), typeof(Emission))]
|
||||||
public class Dispenser : ModuleState
|
public class Dispenser : ModuleState
|
||||||
{
|
{
|
||||||
private static readonly int Open = Animator.StringToHash("Open");
|
private static readonly int Open = Animator.StringToHash("Open");
|
||||||
|
|
||||||
|
[Tooltip("The object this dispenser should produce.")]
|
||||||
public Transform dispensable;
|
public Transform dispensable;
|
||||||
|
[Tooltip("The time in seconds to wait until another object can be produced.")]
|
||||||
[SerializeField] [Min(0)]
|
[SerializeField] [Min(0)]
|
||||||
private float cooldown;
|
private float cooldown;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
@@ -20,6 +25,9 @@ namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
|||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
private Button dispenseButton;
|
private Button dispenseButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The dispenser does not produce any more objects when it is solved and changes colour.
|
||||||
|
/// </summary>
|
||||||
public bool Solved
|
public bool Solved
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -10,8 +10,12 @@ namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
|||||||
public bool[] lights;
|
public bool[] lights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This component controls the grid of lights on the dispenser.
|
||||||
|
/// </summary>
|
||||||
public class DispenserLights : MonoBehaviour
|
public class DispenserLights : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
[Tooltip("Control the pattern of lights on a dispenser.")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private DispenserLightRow[] lights;
|
private DispenserLightRow[] lights;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
@@ -23,7 +27,6 @@ namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
|||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
var position = transform.localPosition;
|
|
||||||
for (var y = 0; y < gridDimensions.y; y++)
|
for (var y = 0; y < gridDimensions.y; y++)
|
||||||
{
|
{
|
||||||
var row = lights[y].lights;
|
var row = lights[y].lights;
|
||||||
|
|||||||
@@ -3,13 +3,23 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime
|
namespace EscapeRoomEngine.Desert.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A general component for controlling the emission of an object.
|
||||||
|
/// </summary>
|
||||||
public class Emission : MonoBehaviour
|
public class Emission : MonoBehaviour
|
||||||
{
|
{
|
||||||
private static readonly int EmissionColorNameID = Shader.PropertyToID("_EmissionColor");
|
private static readonly int EmissionColorNameID = Shader.PropertyToID("_EmissionColor");
|
||||||
|
|
||||||
|
[Tooltip("The colour of the emission.")]
|
||||||
|
[ColorUsage(false, true)]
|
||||||
|
public Color color;
|
||||||
|
[BoxGroup("Internal")] [Required]
|
||||||
|
public MeshRenderer emissionRenderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the emission is on.
|
||||||
|
/// </summary>
|
||||||
internal bool active;
|
internal bool active;
|
||||||
[ColorUsage(false, true)] public Color color;
|
|
||||||
[BoxGroup("Internal")] [Required] public MeshRenderer emissionRenderer;
|
|
||||||
|
|
||||||
private bool _previousActive;
|
private bool _previousActive;
|
||||||
private Color _previousColor;
|
private Color _previousColor;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime
|
namespace EscapeRoomEngine.Desert.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A holographic <see cref="Button"/> that changes its colour when it is pressed.
|
||||||
|
/// </summary>
|
||||||
public class HoloButton : Button
|
public class HoloButton : Button
|
||||||
{
|
{
|
||||||
private static readonly int FresnelColor = Shader.PropertyToID("_FresnelColor");
|
private static readonly int FresnelColor = Shader.PropertyToID("_FresnelColor");
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime
|
namespace EscapeRoomEngine.Desert.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the orb throwing module.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Collider))]
|
[RequireComponent(typeof(Collider))]
|
||||||
public class Hoop : StatePuzzle
|
public class Hoop : StatePuzzle
|
||||||
{
|
{
|
||||||
|
[Tooltip("How long to wait in seconds until the state is updated. Orbs that do not stay in the hoop should not be counted.")]
|
||||||
public float updateDelay;
|
public float updateDelay;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
private List<Emission> rings;
|
private List<Emission> rings;
|
||||||
@@ -84,6 +88,7 @@ namespace EscapeRoomEngine.Desert.Runtime
|
|||||||
{
|
{
|
||||||
base.SetModule(module);
|
base.SetModule(module);
|
||||||
|
|
||||||
|
// The hoop requires a related dispenser module
|
||||||
var firstRelatedModule = Module.relatedModules[0];
|
var firstRelatedModule = Module.relatedModules[0];
|
||||||
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Portal
|
namespace EscapeRoomEngine.Desert.Runtime.Portal
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The desert theme includes its own version of a portal that changes colour when it is unlocked.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Emission))]
|
[RequireComponent(typeof(Emission))]
|
||||||
public class DesertPortal : EscapeRoomEngine.Portal.Runtime.Portal
|
public class DesertPortal : EscapeRoomEngine.Portal.Runtime.Portal
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ using NaughtyAttributes;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the symbol ball module.
|
||||||
|
/// </summary>
|
||||||
public class Ball : ModuleState
|
public class Ball : ModuleState
|
||||||
{
|
{
|
||||||
[BoxGroup("Internal")] [Required] public Emission statusLight;
|
[BoxGroup("Internal")] [Required]
|
||||||
[BoxGroup("Internal")] [Required] public Ring ring;
|
public Emission statusLight;
|
||||||
|
[BoxGroup("Internal")] [Required]
|
||||||
|
public Ring ring;
|
||||||
|
|
||||||
public bool StatusLight
|
public bool StatusLight
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,12 +6,20 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The rotating ring in the symbol ball module.
|
||||||
|
/// </summary>
|
||||||
public class Ring : MonoBehaviour
|
public class Ring : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
[Tooltip("Whether the ring is rotating.")]
|
||||||
public bool rotating = true;
|
public bool rotating = true;
|
||||||
|
[Tooltip("The current angle of the rotation.")]
|
||||||
public float rotationAngle;
|
public float rotationAngle;
|
||||||
[MinMaxSlider(-180, 180)] public Vector2 activeRange;
|
[Tooltip("The range in degrees from the front of the module where a symbol should be lit up.")]
|
||||||
[BoxGroup("Internal")] [Required] public Crystal crystal;
|
[MinMaxSlider(-180, 180)]
|
||||||
|
public Vector2 activeRange;
|
||||||
|
[BoxGroup("Internal")] [Required]
|
||||||
|
public Crystal crystal;
|
||||||
[BoxGroup("Internal")]
|
[BoxGroup("Internal")]
|
||||||
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
||||||
public List<Symbol> symbols;
|
public List<Symbol> symbols;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using NaughtyAttributes;
|
||||||
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;
|
||||||
@@ -27,13 +28,23 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
|||||||
|
|
||||||
public delegate void SymbolEventHandler(SymbolEventType e);
|
public delegate void SymbolEventHandler(SymbolEventType e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the symbols on the symbol ring module.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Emission))]
|
[RequireComponent(typeof(Emission))]
|
||||||
public class Symbol : MonoBehaviour
|
public class Symbol : MonoBehaviour
|
||||||
{
|
{
|
||||||
public int symbolNumber, symbolPosition;
|
[Tooltip("The number of the symbol in the puzzle solution.")]
|
||||||
|
public int symbolNumber;
|
||||||
|
[BoxGroup("Internal")]
|
||||||
|
public int symbolPosition;
|
||||||
|
[BoxGroup("Internal")]
|
||||||
public float anglePosition;
|
public float anglePosition;
|
||||||
public event SymbolEventHandler SymbolEvent;
|
public event SymbolEventHandler SymbolEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the symbol is lit up. This is controlled by the <see cref="Ring"/>.
|
||||||
|
/// </summary>
|
||||||
public bool Active
|
public bool Active
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="HoloButton"/> that includes the number of its assigned symbol.
|
||||||
|
/// </summary>
|
||||||
public class SymbolButton : HoloButton
|
public class SymbolButton : HoloButton
|
||||||
{
|
{
|
||||||
|
[Tooltip("The number of the symbol assigned to this button.")]
|
||||||
public int symbolNumber;
|
public int symbolNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,16 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the terminal module.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Animator))]
|
[RequireComponent(typeof(Animator))]
|
||||||
public class Terminal : CyclicStepPuzzle
|
public class Terminal : CyclicStepPuzzle
|
||||||
{
|
{
|
||||||
private static readonly int LightFlash = Animator.StringToHash("Light Flash");
|
private static readonly int LightFlash = Animator.StringToHash("Light Flash");
|
||||||
|
|
||||||
[BoxGroup("Internal")] [Required] public Emission terminalLight;
|
[BoxGroup("Internal")] [Required]
|
||||||
|
public Emission terminalLight;
|
||||||
[BoxGroup("Internal")]
|
[BoxGroup("Internal")]
|
||||||
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
||||||
public List<SymbolButton> symbols;
|
public List<SymbolButton> symbols;
|
||||||
@@ -82,6 +86,7 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
|||||||
{
|
{
|
||||||
base.SetModule(module);
|
base.SetModule(module);
|
||||||
|
|
||||||
|
// The terminal requires a related symbol ball module
|
||||||
var firstRelatedModule = Module.relatedModules[0];
|
var firstRelatedModule = Module.relatedModules[0];
|
||||||
if (firstRelatedModule.State is Ball ball)
|
if (firstRelatedModule.State is Ball ball)
|
||||||
{
|
{
|
||||||
@@ -95,15 +100,15 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
|||||||
TurnOnLights();
|
TurnOnLights();
|
||||||
}
|
}
|
||||||
|
|
||||||
// required in animation
|
[UsedImplicitly] // required in animation
|
||||||
[UsedImplicitly] internal void TurnOnLights()
|
internal void TurnOnLights()
|
||||||
{
|
{
|
||||||
terminalLight.active = true;
|
terminalLight.active = true;
|
||||||
_ball.StatusLight = true;
|
_ball.StatusLight = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// required in animation
|
[UsedImplicitly] // required in animation
|
||||||
[UsedImplicitly] internal void TurnOffLights()
|
internal void TurnOffLights()
|
||||||
{
|
{
|
||||||
terminalLight.active = false;
|
terminalLight.active = false;
|
||||||
_ball.StatusLight = false;
|
_ball.StatusLight = false;
|
||||||
@@ -131,12 +136,19 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
|||||||
|
|
||||||
#region Debug Buttons
|
#region Debug Buttons
|
||||||
|
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void PressSymbol0() => PressedSymbol(0);
|
[UsedImplicitly]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void PressSymbol1() => PressedSymbol(1);
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void PressSymbol2() => PressedSymbol(2);
|
public void PressSymbol0() => PressedSymbol(0);
|
||||||
|
[UsedImplicitly]
|
||||||
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
|
public void PressSymbol1() => PressedSymbol(1);
|
||||||
|
[UsedImplicitly]
|
||||||
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
|
public void PressSymbol2() => PressedSymbol(2);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
[UsedImplicitly] private bool SymbolCount(List<SymbolButton> list) => list.Count == symbolSlots.Count;
|
[UsedImplicitly] // used for field validation
|
||||||
|
private bool SymbolCount(List<SymbolButton> list) => list.Count == symbolSlots.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,12 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
|||||||
public List<int> stateIndices;
|
public List<int> stateIndices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the hexagon module.
|
||||||
|
/// </summary>
|
||||||
public class PuzzleB : StatePuzzle
|
public class PuzzleB : StatePuzzle
|
||||||
{
|
{
|
||||||
|
[Tooltip("How many seconds the buttons should be disabled until another input is accepted.")]
|
||||||
[SerializeField] [Min(0)]
|
[SerializeField] [Min(0)]
|
||||||
private float buttonCooldown;
|
private float buttonCooldown;
|
||||||
[ValidateInput("CorrectRotatorCount")]
|
[ValidateInput("CorrectRotatorCount")]
|
||||||
@@ -86,6 +90,9 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Switch all rotators specified by a given action.
|
||||||
|
/// </summary>
|
||||||
private void Switch(ButtonAction action)
|
private void Switch(ButtonAction action)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < action.stateIndices.Count; i++)
|
for (var i = 0; i < action.stateIndices.Count; i++)
|
||||||
@@ -99,7 +106,7 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly] // used for field validation
|
||||||
private bool CorrectRotatorCount(List<Rotator> list) => list != null && list.Count == stateCount;
|
private bool CorrectRotatorCount(List<Rotator> list) => list != null && list.Count == stateCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,18 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The rotator component used by the hexagon module.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Emission))]
|
[RequireComponent(typeof(Emission))]
|
||||||
public class Rotator : MonoBehaviour
|
public class Rotator : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Range(0, 359)] public int angle;
|
[Tooltip("The angle this rotator should rotate towards.")]
|
||||||
[Range(0, 1)] public float speed;
|
[Range(0, 359)]
|
||||||
|
public int angle;
|
||||||
|
[Tooltip("How quickly to rotate towards the target angle.")]
|
||||||
|
[Range(0, 1)]
|
||||||
|
public float speed;
|
||||||
|
|
||||||
public Emission Emission { get; private set; }
|
public Emission Emission { get; private set; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hole used in the <see cref="Holes"/> module.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Emission), typeof(Collider))]
|
[RequireComponent(typeof(Emission), typeof(Collider))]
|
||||||
public class Hole : Button
|
public class Hole : Button
|
||||||
{
|
{
|
||||||
|
[Tooltip("The number of this hole, starting from the bottom left.")]
|
||||||
[Min(0)]
|
[Min(0)]
|
||||||
public int number;
|
public int number;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component for the orb grid module.
|
||||||
|
/// </summary>
|
||||||
public class Holes : StatePuzzle
|
public class Holes : StatePuzzle
|
||||||
{
|
{
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
@@ -75,6 +78,7 @@ namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
|||||||
{
|
{
|
||||||
base.SetModule(module);
|
base.SetModule(module);
|
||||||
|
|
||||||
|
// The holes require a related dispenser module
|
||||||
var firstRelatedModule = Module.relatedModules[0];
|
var firstRelatedModule = Module.relatedModules[0];
|
||||||
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using UnityEngine.UIElements;
|
|||||||
namespace EscapeRoomEngine.Engine.Runtime.Editor
|
namespace EscapeRoomEngine.Engine.Runtime.Editor
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="EditorWindow"/> used to test the <see cref="Engine"/>.
|
||||||
|
/// </summary>
|
||||||
public class EngineEditor : EditorWindow
|
public class EngineEditor : EditorWindow
|
||||||
{
|
{
|
||||||
private bool _registeredUpdateEvent;
|
private bool _registeredUpdateEvent;
|
||||||
|
|||||||
@@ -11,9 +11,18 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The engine controls the whole escape room. It generates rooms and manages the puzzle plan.
|
||||||
|
/// </summary>
|
||||||
public class Engine : MonoBehaviour
|
public class Engine : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The active theme of the engine.
|
||||||
|
/// </summary>
|
||||||
public static EngineTheme Theme => Instance.theme;
|
public static EngineTheme Theme => Instance.theme;
|
||||||
|
/// <summary>
|
||||||
|
/// The active instance of the engine.
|
||||||
|
/// </summary>
|
||||||
public static Engine Instance { get; private set; }
|
public static Engine Instance { get; private set; }
|
||||||
|
|
||||||
public delegate void UpdateUIHandler();
|
public delegate void UpdateUIHandler();
|
||||||
@@ -23,11 +32,16 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
public int maxSpaceGenerationTries = 1000;
|
public int maxSpaceGenerationTries = 1000;
|
||||||
[Tooltip("The engine will try to generate a room that takes approximately this many seconds to complete.")]
|
[Tooltip("The engine will try to generate a room that takes approximately this many seconds to complete.")]
|
||||||
public float initialTargetTime = 10 * 60;
|
public float initialTargetTime = 10 * 60;
|
||||||
|
[Tooltip("The offset each room will have from the previous one.")]
|
||||||
public Vector3 roomOffset = new(0, 1000, 0);
|
public Vector3 roomOffset = new(0, 1000, 0);
|
||||||
|
[Tooltip("The theme of the engine that decides the available puzzles, doors and more.")]
|
||||||
[Required] public EngineTheme theme;
|
[Required] public EngineTheme theme;
|
||||||
|
|
||||||
public int NumberOfRooms => _rooms.Count;
|
public int NumberOfRooms => _rooms.Count;
|
||||||
public IOption<Room> CurrentRoom => NumberOfRooms > 0 ? Some<Room>.Of(_rooms[^1]) : None<Room>.New();
|
public IOption<Room> CurrentRoom => NumberOfRooms > 0 ? Some<Room>.Of(_rooms[^1]) : None<Room>.New();
|
||||||
|
/// <summary>
|
||||||
|
/// The currendly estimated time for the player to finish the experience.
|
||||||
|
/// </summary>
|
||||||
public float EstimatedTimeRemaining { get; private set; }
|
public float EstimatedTimeRemaining { get; private set; }
|
||||||
|
|
||||||
private readonly List<Room> _rooms = new();
|
private readonly List<Room> _rooms = new();
|
||||||
@@ -169,6 +183,10 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
GameControl.Instance.PlannedPuzzles = _plannedPuzzles;
|
GameControl.Instance.PlannedPuzzles = _plannedPuzzles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide or destroy the room two rooms ago. The actual previous room is kept for the player to be able to backtrack one room.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destroy"></param>
|
||||||
public void HidePreviousRoom(bool destroy = true)
|
public void HidePreviousRoom(bool destroy = true)
|
||||||
{
|
{
|
||||||
if (NumberOfRooms > 2)
|
if (NumberOfRooms > 2)
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An engine theme decides the room size, look and available modules for the engine.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Engine Theme")]
|
[CreateAssetMenu(menuName = "Engine Theme")]
|
||||||
public class EngineTheme : ScriptableObject
|
public class EngineTheme : ScriptableObject
|
||||||
{
|
{
|
||||||
@@ -23,9 +26,11 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
#region Theme
|
#region Theme
|
||||||
|
|
||||||
[BoxGroup("Theme")] [Required]
|
[BoxGroup("Theme")] [Required]
|
||||||
|
[Tooltip("The tile that rooms are generated from.")]
|
||||||
public SpaceTile spaceTile;
|
public SpaceTile spaceTile;
|
||||||
|
|
||||||
[BoxGroup("Theme")]
|
[BoxGroup("Theme")]
|
||||||
|
[Tooltip("The environment that is placed around every generated room.")]
|
||||||
public GameObject environment;
|
public GameObject environment;
|
||||||
|
|
||||||
[BoxGroup("Theme")]
|
[BoxGroup("Theme")]
|
||||||
@@ -39,6 +44,7 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
public DoorModuleDescription spawnDoor;
|
public DoorModuleDescription spawnDoor;
|
||||||
|
|
||||||
[BoxGroup("Doors")] [ValidateInput("IsNotEmpty", "At least one exit door type is required.")]
|
[BoxGroup("Doors")] [ValidateInput("IsNotEmpty", "At least one exit door type is required.")]
|
||||||
|
[Tooltip("The types of exit doors this theme provides. Entrance doors are connected to the exit doors and don't need to be specified here.")]
|
||||||
public List<DoorModuleDescription> exitDoorTypes;
|
public List<DoorModuleDescription> exitDoorTypes;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is the main way to interact with measurements.
|
||||||
|
/// </summary>
|
||||||
public static class Measure
|
public static class Measure
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -19,20 +22,46 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
private static Dictionary<int, PuzzleMeasurement> _runningMeasurements;
|
private static Dictionary<int, PuzzleMeasurement> _runningMeasurements;
|
||||||
private static Session _currentSession;
|
private static Session _currentSession;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The average time to solve a specific puzzle.
|
||||||
|
/// </summary>
|
||||||
public static float AverageTime(PuzzleModuleDescription puzzle) =>
|
public static float AverageTime(PuzzleModuleDescription puzzle) =>
|
||||||
PuzzleStorage.Instance.Load(puzzle).AverageTimeToSolve;
|
PuzzleStorage.Instance.Load(puzzle).AverageTimeToSolve;
|
||||||
|
/// <summary>
|
||||||
|
/// The average time to solve a group of puzzles.
|
||||||
|
/// </summary>
|
||||||
public static float AverageTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(AverageTime);
|
public static float AverageTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(AverageTime);
|
||||||
|
/// <summary>
|
||||||
|
/// The average time to solve a specific room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This only counts puzzles already placed in a room.</remarks>
|
||||||
public static float AverageTime(Room room) =>
|
public static float AverageTime(Room room) =>
|
||||||
room.puzzles.Sum(puzzle => AverageTime((PuzzleModuleDescription)puzzle.description));
|
room.puzzles.Sum(puzzle => AverageTime((PuzzleModuleDescription)puzzle.description));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Estimate the time a specific puzzle will take to be solved by the current player.
|
||||||
|
/// </summary>
|
||||||
public static float EstimateTime(PuzzleModuleDescription puzzle) =>
|
public static float EstimateTime(PuzzleModuleDescription puzzle) =>
|
||||||
PuzzleStorage.Instance.Load(puzzle).EstimateTimeToSolve(SessionPercentile());
|
PuzzleStorage.Instance.Load(puzzle).EstimateTimeToSolve(SessionPercentile());
|
||||||
|
/// <summary>
|
||||||
|
/// Estimate the time a group of puzzles will take to be solved by the current player.
|
||||||
|
/// </summary>
|
||||||
public static float EstimateTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(EstimateTime);
|
public static float EstimateTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(EstimateTime);
|
||||||
|
/// <summary>
|
||||||
|
/// Estimate the time a room will take to be solved by the current player.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This only counts puzzles already placed in a room.</remarks>
|
||||||
public static float EstimateTime(Room room) =>
|
public static float EstimateTime(Room room) =>
|
||||||
room.puzzles.Sum(puzzle => EstimateTime((PuzzleModuleDescription)puzzle.description));
|
room.puzzles.Sum(puzzle => EstimateTime((PuzzleModuleDescription)puzzle.description));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The estimated percentile the current player is placed in.
|
||||||
|
/// </summary>
|
||||||
public static float SessionPercentile() => _currentSession?.MeanPercentile ?? 0.5f;
|
public static float SessionPercentile() => _currentSession?.MeanPercentile ?? 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start counting the time to solve a specific puzzle.
|
||||||
|
/// </summary>
|
||||||
public static void StartMeasuring(PuzzleModuleDescription puzzle)
|
public static void StartMeasuring(PuzzleModuleDescription puzzle)
|
||||||
{
|
{
|
||||||
_runningMeasurements[puzzle.Id] = new PuzzleMeasurement
|
_runningMeasurements[puzzle.Id] = new PuzzleMeasurement
|
||||||
@@ -44,6 +73,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
Logger.Log($"Started measuring {puzzle}", LogType.Measuring);
|
Logger.Log($"Started measuring {puzzle}", LogType.Measuring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A puzzle has been solved and the measurement should be stopped. This will also store the finished measurement in the database.
|
||||||
|
/// </summary>
|
||||||
public static void Solve(PuzzleModuleDescription puzzle)
|
public static void Solve(PuzzleModuleDescription puzzle)
|
||||||
{
|
{
|
||||||
if (_currentSession == null)
|
if (_currentSession == null)
|
||||||
@@ -62,6 +94,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
Logger.Log($"Solved {puzzle} with measurement {measurement}", LogType.Measuring);
|
Logger.Log($"Solved {puzzle} with measurement {measurement}", LogType.Measuring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start a new session and place the current player at the 50th percentile.
|
||||||
|
/// </summary>
|
||||||
public static void StartSession()
|
public static void StartSession()
|
||||||
{
|
{
|
||||||
_currentSession = new Session();
|
_currentSession = new Session();
|
||||||
@@ -75,6 +110,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
Logger.Log($"Started {_currentSession}", LogType.Measuring);
|
Logger.Log($"Started {_currentSession}", LogType.Measuring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End a session and store it in the database.
|
||||||
|
/// </summary>
|
||||||
public static void EndSession(float time)
|
public static void EndSession(float time)
|
||||||
{
|
{
|
||||||
if (Store)
|
if (Store)
|
||||||
|
|||||||
@@ -3,10 +3,22 @@ using Realms;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The target and estimated time, along with the percentile since the last section, that will be stored in the database.
|
||||||
|
/// </summary>
|
||||||
public class PlanResult : EmbeddedObject
|
public class PlanResult : EmbeddedObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The target time set by the game master when this section ended.
|
||||||
|
/// </summary>
|
||||||
public float TargetTime { get; set; }
|
public float TargetTime { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The percentile of the current player during this section.
|
||||||
|
/// </summary>
|
||||||
public float SectionPercentile { get; set; }
|
public float SectionPercentile { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The estimated time to complete the whole puzzle plan when this section ended.
|
||||||
|
/// </summary>
|
||||||
public float TimeEstimation { get; set; }
|
public float TimeEstimation { get; set; }
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
|
|||||||
@@ -8,15 +8,30 @@ using Realms;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The representation of a specific puzzle in the database. This includes all measurements for this puzzle.
|
||||||
|
/// </summary>
|
||||||
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
||||||
public class Puzzle : RealmObject
|
public class Puzzle : RealmObject
|
||||||
{
|
{
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All puzzle measurements recorded for this puzzle.
|
||||||
|
/// </summary>
|
||||||
public IList<PuzzleMeasurement> Measurements { get; }
|
public IList<PuzzleMeasurement> Measurements { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much time has been spent on this puzzle in total. This is used to calculate the average time to solve this puzzle.
|
||||||
|
/// </summary>
|
||||||
public float TotalTimeSpentOnPuzzle => Measurements.Sum(measurement => measurement.Time);
|
public float TotalTimeSpentOnPuzzle => Measurements.Sum(measurement => measurement.Time);
|
||||||
|
/// <summary>
|
||||||
|
/// The average time to solve this puzzle using all measurements recorded for it.
|
||||||
|
/// </summary>
|
||||||
public float AverageTimeToSolve => Measurements.Count > 0 ? TotalTimeSpentOnPuzzle / Measurements.Count : 0f;
|
public float AverageTimeToSolve => Measurements.Count > 0 ? TotalTimeSpentOnPuzzle / Measurements.Count : 0f;
|
||||||
|
/// <summary>
|
||||||
|
/// The normal distribution of all recorded measurements.
|
||||||
|
/// </summary>
|
||||||
public NormalDistribution Distribution => new(TimesAsSamples());
|
public NormalDistribution Distribution => new(TimesAsSamples());
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
|
|||||||
@@ -3,11 +3,23 @@ using Realms;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A single measurement, consisting of when a puzzle was started and solved, stored in the database.
|
||||||
|
/// </summary>
|
||||||
public class PuzzleMeasurement : EmbeddedObject
|
public class PuzzleMeasurement : EmbeddedObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The relative time since the engine was initialised of when this puzzle was started.
|
||||||
|
/// </summary>
|
||||||
public float TimeStarted { get; set; }
|
public float TimeStarted { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The relative time since the engine was initialised of when this puzzle was solved.
|
||||||
|
/// </summary>
|
||||||
public float TimeSolved { get; set; }
|
public float TimeSolved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total time taken to solve this puzzle during this measurement.
|
||||||
|
/// </summary>
|
||||||
public float Time => TimeSolved - TimeStarted;
|
public float Time => TimeSolved - TimeStarted;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -7,12 +7,22 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The storage engine that manages the data in a realm database.
|
||||||
|
/// </summary>
|
||||||
public class PuzzleStorage : MonoBehaviour
|
public class PuzzleStorage : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The schema version must be updated whenever the schema in the database changed.
|
||||||
|
/// </summary>
|
||||||
private const int SchemaVersion = 2;
|
private const int SchemaVersion = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The active instance of the storage engine.
|
||||||
|
/// </summary>
|
||||||
public static PuzzleStorage Instance { get; private set; }
|
public static PuzzleStorage Instance { get; private set; }
|
||||||
|
|
||||||
|
[Tooltip("The path of the database relative to where the engine stores persistent data.")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private string databasePath = "measurements.realm";
|
private string databasePath = "measurements.realm";
|
||||||
|
|
||||||
@@ -20,6 +30,7 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
|
// configure special actions during specific migrations of the database
|
||||||
var config = new RealmConfiguration
|
var config = new RealmConfiguration
|
||||||
{
|
{
|
||||||
SchemaVersion = SchemaVersion,
|
SchemaVersion = SchemaVersion,
|
||||||
@@ -59,6 +70,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
|
|
||||||
#region Session
|
#region Session
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End a session and store it to the database.
|
||||||
|
/// </summary>
|
||||||
public void EndSession(Session session, float time)
|
public void EndSession(Session session, float time)
|
||||||
{
|
{
|
||||||
session.Time = time;
|
session.Time = time;
|
||||||
@@ -70,6 +84,10 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
|
|
||||||
#region Puzzles
|
#region Puzzles
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new puzzle for a specific description and store it in the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This requires that the puzzle does not yet exist in the database.</remarks>
|
||||||
private Puzzle New(PuzzleModuleDescription puzzle)
|
private Puzzle New(PuzzleModuleDescription puzzle)
|
||||||
{
|
{
|
||||||
Puzzle created = null;
|
Puzzle created = null;
|
||||||
@@ -79,10 +97,20 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load a specific puzzle from the database or create it if it wasn't found.
|
||||||
|
/// </summary>
|
||||||
private Puzzle LoadOrNew(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id) ?? New(puzzle);
|
private Puzzle LoadOrNew(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id) ?? New(puzzle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load a specific puzzle from the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The puzzle or null if it wasn't found in the database.</returns>
|
||||||
public Puzzle Load(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id);
|
public Puzzle Load(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete all records concerning a specific puzzle from the database irreversibly.
|
||||||
|
/// </summary>
|
||||||
public void Delete(PuzzleModuleDescription puzzle)
|
public void Delete(PuzzleModuleDescription puzzle)
|
||||||
{
|
{
|
||||||
var found = Load(puzzle);
|
var found = Load(puzzle);
|
||||||
@@ -92,6 +120,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End the measurement for a specific puzzle and store it in the database. Also stores the results of the current puzzle plan.
|
||||||
|
/// </summary>
|
||||||
public void EndMeasurement(Session session, PuzzleModuleDescription puzzle, PuzzleMeasurement measurement)
|
public void EndMeasurement(Session session, PuzzleModuleDescription puzzle, PuzzleMeasurement measurement)
|
||||||
{
|
{
|
||||||
var found = LoadOrNew(puzzle);
|
var found = LoadOrNew(puzzle);
|
||||||
|
|||||||
@@ -8,16 +8,33 @@ using Realms;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
namespace EscapeRoomEngine.Engine.Runtime.Measurements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The representation of a session in the database. This stores all plan results during this session and what puzzles were solved.
|
||||||
|
/// </summary>
|
||||||
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
||||||
public class Session : RealmObject
|
public class Session : RealmObject
|
||||||
{
|
{
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public ObjectId ID { get; set; }
|
public ObjectId ID { get; set; }
|
||||||
public float Time { get; set; }
|
public float Time { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The plan results for each section during this section.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The order of the plan results corresponds with the order of the solved puzzles.</remarks>
|
||||||
public IList<PlanResult> PlanResults { get; }
|
public IList<PlanResult> PlanResults { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The puzzles solved during this session.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The order of the solved puzzles corresponds with the order of the plan results.</remarks>
|
||||||
public IList<Puzzle> PuzzlesSolved { get; }
|
public IList<Puzzle> PuzzlesSolved { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The section percentiles the current player has been placed in.
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<float> Percentiles => PlanResults.Select(result => result.SectionPercentile);
|
public IEnumerable<float> Percentiles => PlanResults.Select(result => result.SectionPercentile);
|
||||||
|
/// <summary>
|
||||||
|
/// The average of all section percentiles.
|
||||||
|
/// </summary>
|
||||||
public float MeanPercentile => PlanResults.Count == 0 ? .5f : Probability.Mean(Percentiles.ToArray());
|
public float MeanPercentile => PlanResults.Count == 0 ? .5f : Probability.Mean(Percentiles.ToArray());
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
|
|||||||
@@ -3,9 +3,14 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="StepPuzzle"/> whose steps are considered a cycle that can be started anywhere and must be stepped through once.
|
||||||
|
/// </summary>
|
||||||
public class CyclicStepPuzzle : StepPuzzle
|
public class CyclicStepPuzzle : StepPuzzle
|
||||||
{
|
{
|
||||||
[BoxGroup("Step Puzzle")] [Min(0)] public int cycleStep;
|
[Tooltip("The current step in the cycle.")]
|
||||||
|
[BoxGroup("Step Puzzle")] [Min(0)]
|
||||||
|
public int cycleStep;
|
||||||
|
|
||||||
protected override void CheckStep(int step)
|
protected override void CheckStep(int step)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,13 +9,22 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
Entrance = ModuleType.DoorEntrance, Exit = ModuleType.DoorExit
|
Entrance = ModuleType.DoorEntrance, Exit = ModuleType.DoorExit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main component of any door module.
|
||||||
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class DoorModule : Module
|
public class DoorModule : Module
|
||||||
{
|
{
|
||||||
public bool IsEntrance => IsType((ModuleType)DoorType.Entrance);
|
public bool IsEntrance => IsType((ModuleType)DoorType.Entrance);
|
||||||
public bool IsExit => IsType((ModuleType)DoorType.Exit);
|
public bool IsExit => IsType((ModuleType)DoorType.Exit);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The module state of this door as a <see cref="DoorState"/>.
|
||||||
|
/// </summary>
|
||||||
public DoorState DoorState => DoorState.FromState(State);
|
public DoorState DoorState => DoorState.FromState(State);
|
||||||
|
/// <summary>
|
||||||
|
/// The module state of the connected door as a <see cref="DoorState"/>.
|
||||||
|
/// </summary>
|
||||||
public DoorState ConnectedDoorState => Passage.Other(this).DoorState;
|
public DoorState ConnectedDoorState => Passage.Other(this).DoorState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,6 +56,7 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
{
|
{
|
||||||
base.InstantiateModule(parent);
|
base.InstantiateModule(parent);
|
||||||
|
|
||||||
|
// the room needs to know about this door
|
||||||
space.room.AddDoor(this);
|
space.room.AddDoor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ModuleDescription"/> for a <see cref="DoorModule"/>. Includes the description of the connected door.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Modules/Door")]
|
[CreateAssetMenu(menuName = "Modules/Door")]
|
||||||
public class DoorModuleDescription : ModuleDescription
|
public class DoorModuleDescription : ModuleDescription
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,10 +10,19 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
|
|
||||||
public delegate void DoorEventHandler(DoorModule source, DoorEventType e);
|
public delegate void DoorEventHandler(DoorModule source, DoorEventType e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ModuleState"/> of a <see cref="DoorModule"/>. Handles all events that can occur for a door.
|
||||||
|
/// </summary>
|
||||||
public class DoorState : ModuleState
|
public class DoorState : ModuleState
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add event handlers to this hook to receive all events concerning this door.
|
||||||
|
/// </summary>
|
||||||
public event DoorEventHandler DoorEvent;
|
public event DoorEventHandler DoorEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The door module this state belongs to.
|
||||||
|
/// </summary>
|
||||||
protected DoorModule Module { get; set; }
|
protected DoorModule Module { get; set; }
|
||||||
public bool Unlocked
|
public bool Unlocked
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ using Object = UnityEngine.Object;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main representation of a module. Contains references to the module description, state and handles its placement.
|
||||||
|
/// </summary>
|
||||||
public class Module
|
public class Module
|
||||||
{
|
{
|
||||||
public readonly List<Module> relatedModules = new();
|
public readonly List<Module> relatedModules = new();
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The description of a specific module variant. Includes any requirements, the types and the module state that should be initialised with the module.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Modules/Generic Module")]
|
[CreateAssetMenu(menuName = "Modules/Generic Module")]
|
||||||
public class ModuleDescription : ScriptableObject
|
public class ModuleDescription : ScriptableObject
|
||||||
{
|
{
|
||||||
|
[Tooltip("The module types decide how this module can be used.")]
|
||||||
public List<ModuleType> types = new();
|
public List<ModuleType> types = new();
|
||||||
public ModuleState modulePrefab;
|
public ModuleState modulePrefab;
|
||||||
[BoxGroup("Requirements")]
|
[BoxGroup("Requirements")]
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An abstract module state. Example implementations are <see cref="DoorState"/> and <see cref="PuzzleState"/>.
|
||||||
|
/// </summary>
|
||||||
public abstract class ModuleState : MonoBehaviour
|
public abstract class ModuleState : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
[Tooltip("The size of this module in meters.")]
|
||||||
public Vector2Int size = Vector2Int.one;
|
public Vector2Int size = Vector2Int.one;
|
||||||
|
|
||||||
public abstract void SetModule(Module module);
|
public abstract void SetModule(Module module);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A module can have one or multiple types.
|
||||||
|
/// </summary>
|
||||||
public enum ModuleType
|
public enum ModuleType
|
||||||
{
|
{
|
||||||
DoorEntrance, DoorExit, // door types
|
DoorEntrance, DoorExit, // door types
|
||||||
|
|||||||
@@ -3,8 +3,14 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main component of any puzzle module.
|
||||||
|
/// </summary>
|
||||||
public class PuzzleModule : Module
|
public class PuzzleModule : Module
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The module state of this puzzle as a <see cref="PuzzleState"/>.
|
||||||
|
/// </summary>
|
||||||
internal PuzzleState PuzzleState => PuzzleState.FromState(State);
|
internal PuzzleState PuzzleState => PuzzleState.FromState(State);
|
||||||
|
|
||||||
internal PuzzleModule(Space space, PuzzleModuleDescription description) : base(space, description) {}
|
internal PuzzleModule(Space space, PuzzleModuleDescription description) : base(space, description) {}
|
||||||
@@ -13,6 +19,7 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
{
|
{
|
||||||
base.InstantiateModule(parent);
|
base.InstantiateModule(parent);
|
||||||
|
|
||||||
|
// the room needs to know about this puzzle
|
||||||
space.room.AddPuzzle(this);
|
space.room.AddPuzzle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,21 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ModuleDescription"/> for a <see cref="DoorModule"/>. Includes the description of the connected door.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Modules/Puzzle")]
|
[CreateAssetMenu(menuName = "Modules/Puzzle")]
|
||||||
public class PuzzleModuleDescription : ModuleDescription
|
public class PuzzleModuleDescription : ModuleDescription
|
||||||
{
|
{
|
||||||
|
[Tooltip("Each puzzle has a name that will be used to display it to the game master.")]
|
||||||
|
[InfoBox("Changes to the name or version of the puzzle change its ID, which means it will not be connected with any of its previous measures any more.", EInfoBoxType.Warning)]
|
||||||
public string puzzleName;
|
public string puzzleName;
|
||||||
[InfoBox("Puzzle measurements will only be combined for the same puzzle with the same version. If large changes are made to the puzzle that might influence its measurements, the version should be incremented.")]
|
[InfoBox("Puzzle measurements will only be combined for the same puzzle with the same version. If large changes are made to the puzzle that might influence its measurements, the version should be incremented.")]
|
||||||
public int puzzleVersion = 1;
|
public int puzzleVersion = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the puzzle is used in the database.
|
||||||
|
/// </summary>
|
||||||
public int Id => puzzleName.GetHashCode() + puzzleVersion;
|
public int Id => puzzleName.GetHashCode() + puzzleVersion;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using NaughtyAttributes;
|
using NaughtyAttributes;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||||
@@ -28,11 +29,20 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
|
|
||||||
public delegate void PuzzleEventHandler(PuzzleModule source, PuzzleEventType e);
|
public delegate void PuzzleEventHandler(PuzzleModule source, PuzzleEventType e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ModuleState"/> of a <see cref="PuzzleModule"/>. Handles all events that can occur for a door.
|
||||||
|
/// </summary>
|
||||||
[SelectionBase]
|
[SelectionBase]
|
||||||
public class PuzzleState : ModuleState
|
public class PuzzleState : ModuleState
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add event handlers to this hook to receive all events concerning this puzzle.
|
||||||
|
/// </summary>
|
||||||
public event PuzzleEventHandler PuzzleEvent;
|
public event PuzzleEventHandler PuzzleEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The puzzle module this state belongs to.
|
||||||
|
/// </summary>
|
||||||
protected PuzzleModule Module { get; private set; }
|
protected PuzzleModule Module { get; private set; }
|
||||||
public bool Solved
|
public bool Solved
|
||||||
{
|
{
|
||||||
@@ -69,8 +79,11 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
|
|
||||||
#region Debug Buttons
|
#region Debug Buttons
|
||||||
|
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Solve() => Solved = true;
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
[Button(enabledMode: EButtonEnableMode.Playmode)] public void Restart() => Solved = false;
|
public void Solve() => Solved = true;
|
||||||
|
[UsedImplicitly]
|
||||||
|
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||||
|
public void Restart() => Solved = false;
|
||||||
[Button("Trigger Wrong Input", EButtonEnableMode.Playmode)]
|
[Button("Trigger Wrong Input", EButtonEnableMode.Playmode)]
|
||||||
public void WrongInput()
|
public void WrongInput()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The spawn door is used in the first room.
|
||||||
|
/// </summary>
|
||||||
internal class Spawn : DoorState
|
internal class Spawn : DoorState
|
||||||
{
|
{
|
||||||
private void Start()
|
private void Start()
|
||||||
|
|||||||
@@ -6,11 +6,20 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="PuzzleState"/> of a puzzle that requires a specific state to be solved.
|
||||||
|
/// </summary>
|
||||||
public class StatePuzzle : PuzzleState
|
public class StatePuzzle : PuzzleState
|
||||||
{
|
{
|
||||||
|
[Tooltip("The state this puzzle starts at.")]
|
||||||
[BoxGroup("State Puzzle")]
|
[BoxGroup("State Puzzle")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
protected List<int> states, solution;
|
protected List<int> states;
|
||||||
|
[Tooltip("The state that needs to be reached for this puzzle to be solved.")]
|
||||||
|
[BoxGroup("State Puzzle")]
|
||||||
|
[SerializeField]
|
||||||
|
protected List<int> solution;
|
||||||
|
[Tooltip("Set this to the total number of states this puzzle has.")]
|
||||||
[BoxGroup("State Puzzle")]
|
[BoxGroup("State Puzzle")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
[ValidateInput("CorrectStateCount", "States count must be equal to the number of states and the length of the solution.")]
|
[ValidateInput("CorrectStateCount", "States count must be equal to the number of states and the length of the solution.")]
|
||||||
@@ -76,7 +85,7 @@ namespace EscapeRoomEngine.Engine.Runtime.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly] // used for field validation
|
||||||
private bool CorrectStateCount(int count) =>
|
private bool CorrectStateCount(int count) =>
|
||||||
states != null && count == states.Count &&
|
states != null && count == states.Count &&
|
||||||
solution != null && count == solution.Count;
|
solution != null && count == solution.Count;
|
||||||
|
|||||||
@@ -6,12 +6,16 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
namespace EscapeRoomEngine.Engine.Runtime.Modules
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="PuzzleState"/> of a puzzle that includes a number of steps that need to be completed to solve it.
|
||||||
|
/// </summary>
|
||||||
public abstract class StepPuzzle : PuzzleState
|
public abstract class StepPuzzle : PuzzleState
|
||||||
{
|
{
|
||||||
[BoxGroup("Step Puzzle")]
|
[BoxGroup("Step Puzzle")]
|
||||||
[InfoBox("In easy mode, the step puzzle will not reset if a wrong input is made.")]
|
[InfoBox("In easy mode, the step puzzle will not reset if a wrong input is made.")]
|
||||||
public bool easyMode;
|
public bool easyMode;
|
||||||
[BoxGroup("Step Puzzle")] [Min(0)] public int totalSteps;
|
[BoxGroup("Step Puzzle")] [Min(0)]
|
||||||
|
public int totalSteps;
|
||||||
[BoxGroup("Step Puzzle")] [ProgressBar("Step", "totalSteps", EColor.Orange)]
|
[BoxGroup("Step Puzzle")] [ProgressBar("Step", "totalSteps", EColor.Orange)]
|
||||||
public int currentStep;
|
public int currentStep;
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,24 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
Orientation.North, Orientation.East, Orientation.South, Orientation.West
|
Orientation.North, Orientation.East, Orientation.South, Orientation.West
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the orientation corresponding to an angle, starting at 0 degrees North and going clockwise around the compass.
|
||||||
|
/// </summary>
|
||||||
public static Orientation FromAngle(int angle) => (Orientation)angle;
|
public static Orientation FromAngle(int angle) => (Orientation)angle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Turn an orientation into its corresponding angle.
|
||||||
|
/// </summary>
|
||||||
public static float Angle(this Orientation orientation) => (float)orientation;
|
public static float Angle(this Orientation orientation) => (float)orientation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Turn an orientation into its corresponding angle.
|
||||||
|
/// </summary>
|
||||||
public static int AngleInt(this Orientation orientation) => (int)orientation;
|
public static int AngleInt(this Orientation orientation) => (int)orientation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate an orientation by a specific amount of quarter rotations. Rotating <c>East</c> thrice would return <c>North</c>.
|
||||||
|
/// </summary>
|
||||||
public static Orientation Rotated(this Orientation orientation, int quarterRotations) =>
|
public static Orientation Rotated(this Orientation orientation, int quarterRotations) =>
|
||||||
orientation + quarterRotations * 90;
|
orientation + quarterRotations * 90;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,18 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The passage handles the two door modules that connect two spaces.
|
||||||
|
/// </summary>
|
||||||
public class Passage
|
public class Passage
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The exit door in the previous room.
|
||||||
|
/// </summary>
|
||||||
internal readonly DoorModule fromOut;
|
internal readonly DoorModule fromOut;
|
||||||
|
/// <summary>
|
||||||
|
/// The entrance door in the next room.
|
||||||
|
/// </summary>
|
||||||
internal DoorModule toIn;
|
internal DoorModule toIn;
|
||||||
|
|
||||||
internal Passage(DoorModule from)
|
internal Passage(DoorModule from)
|
||||||
@@ -20,6 +29,9 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
fromOut = from;
|
fromOut = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Place an entrance door in the same position relative to the room origin as the exit door.
|
||||||
|
/// </summary>
|
||||||
internal void PlaceEntrance(DoorModule door)
|
internal void PlaceEntrance(DoorModule door)
|
||||||
{
|
{
|
||||||
if (!door.IsEntrance)
|
if (!door.IsEntrance)
|
||||||
@@ -35,6 +47,9 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
Logger.Log($"Placed entrance {toIn} at {toIn.RrPosition} (RR)", LogType.ModulePlacement);
|
Logger.Log($"Placed entrance {toIn} at {toIn.RrPosition} (RR)", LogType.ModulePlacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connect the two doors in this passage.
|
||||||
|
/// </summary>
|
||||||
internal void ConnectDoors()
|
internal void ConnectDoors()
|
||||||
{
|
{
|
||||||
toIn.Passage = this;
|
toIn.Passage = this;
|
||||||
@@ -43,6 +58,9 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
Logger.Log($"Connected passage from {fromOut} to {toIn}", LogType.PassageConnection);
|
Logger.Log($"Connected passage from {fromOut} to {toIn}", LogType.PassageConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the door connected to a given door in this passage.
|
||||||
|
/// </summary>
|
||||||
internal DoorModule Other(DoorModule of)
|
internal DoorModule Other(DoorModule of)
|
||||||
{
|
{
|
||||||
if (of.Equals(fromOut))
|
if (of.Equals(fromOut))
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using Range = EscapeRoomEngine.Engine.Runtime.Utilities.Range;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A placement defines the position, size and orientation of a module or space.
|
||||||
|
/// </summary>
|
||||||
public struct Placement
|
public struct Placement
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -16,6 +19,9 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
public Vector2Int size;
|
public Vector2Int size;
|
||||||
public Orientation orientation;
|
public Orientation orientation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the bottom left corner position of the module.
|
||||||
|
/// </summary>
|
||||||
public Vector3Int BottomLeft {
|
public Vector3Int BottomLeft {
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement guarantees that the module faces the center of the space.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Face Space Center")]
|
[CreateAssetMenu(menuName = "Requirements/Face Space Center")]
|
||||||
public class FaceSpaceCenter : PlacementRequirement
|
public class FaceSpaceCenter : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement forces a specific orientation.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Lock Orientation")]
|
[CreateAssetMenu(menuName = "Requirements/Lock Orientation")]
|
||||||
public class LockOrientation : PlacementRequirement
|
public class LockOrientation : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement prevents modules from overlapping. For two models not to overlap, both of them need this requirement.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/No Overlap")]
|
[CreateAssetMenu(menuName = "Requirements/No Overlap")]
|
||||||
public class NoOverlap : PlacementRequirement
|
public class NoOverlap : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement guarantees that the back side of the module is placed at the edge of the space.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Place Along Space Edges")]
|
[CreateAssetMenu(menuName = "Requirements/Place Along Space Edges")]
|
||||||
public class PlaceAlongSpaceEdges : PlacementRequirement
|
public class PlaceAlongSpaceEdges : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement allows any placement. Used for testing purposes.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Place Anywhere")]
|
[CreateAssetMenu(menuName = "Requirements/Place Anywhere")]
|
||||||
public class PlaceAnywhere : PlacementRequirement
|
public class PlaceAnywhere : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement places a module exactly in the same position as its first related module.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Place With Related Module")]
|
[CreateAssetMenu(menuName = "Requirements/Place With Related Module")]
|
||||||
public class PlaceWithRelatedModule : PlacementRequirement
|
public class PlaceWithRelatedModule : PlacementRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using EscapeRoomEngine.Engine.Runtime.Utilities;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main class any placement requirement inherits from. To place a module, every possible placement inside the space is put into a list of candidates that is then filtered by all placement requirements.
|
||||||
|
/// </summary>
|
||||||
public abstract class PlacementRequirement : Requirement<Placement>
|
public abstract class PlacementRequirement : Requirement<Placement>
|
||||||
{
|
{
|
||||||
protected abstract override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space);
|
protected abstract override List<Placement> FilterCandidates(List<Placement> candidates, Module module, Space space);
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main class any precondition requirement inherits from. To place a module, each precondition requirement must return the true-set.
|
||||||
|
/// </summary>
|
||||||
public abstract class PreconditionRequirement : Requirement<bool>
|
public abstract class PreconditionRequirement : Requirement<bool>
|
||||||
{
|
{
|
||||||
private static readonly List<bool> TrueSet = new(new[] { true });
|
private static readonly List<bool> TrueSet = new(new[] { true });
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This requirement guarantees that a module is placed in the same space as its related module.
|
||||||
|
/// </summary>
|
||||||
[CreateAssetMenu(menuName = "Requirements/Related Module")]
|
[CreateAssetMenu(menuName = "Requirements/Related Module")]
|
||||||
class RelatedModule : PreconditionRequirement
|
class RelatedModule : PreconditionRequirement
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
namespace EscapeRoomEngine.Engine.Runtime.Requirements
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A generic requirement class that defines how requirements filter candidates.
|
||||||
|
/// </summary>
|
||||||
public abstract class Requirement<T> : ScriptableObject
|
public abstract class Requirement<T> : ScriptableObject
|
||||||
{
|
{
|
||||||
protected abstract List<T> FilterCandidates(List<T> candidates, Module module, Space space);
|
protected abstract List<T> FilterCandidates(List<T> candidates, Module module, Space space);
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A room manages an entrance to its first space and exit from its last space. It is also responsible for instantiating all spaces in it.
|
||||||
|
/// </summary>
|
||||||
public class Room
|
public class Room
|
||||||
{
|
{
|
||||||
internal Passage entrance, exit;
|
internal Passage entrance, exit;
|
||||||
internal GameObject roomObject;
|
internal GameObject roomObject;
|
||||||
internal readonly List<PuzzleModule> puzzles = new();
|
internal readonly List<PuzzleModule> puzzles = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return whether this room is the last room of the experience, determined by whether this room has an exit.
|
||||||
|
/// </summary>
|
||||||
internal bool LastRoom => exit == null;
|
internal bool LastRoom => exit == null;
|
||||||
|
|
||||||
private readonly List<Space> _spaces = new();
|
private readonly List<Space> _spaces = new();
|
||||||
|
|||||||
@@ -10,12 +10,18 @@ using Range = EscapeRoomEngine.Engine.Runtime.Utilities.Range;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The space is responsible for placing and instantiating any modules in it.
|
||||||
|
/// </summary>
|
||||||
public class Space
|
public class Space
|
||||||
{
|
{
|
||||||
/// <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 Placement rrPlacement;
|
internal readonly Placement rrPlacement;
|
||||||
|
/// <summary>
|
||||||
|
/// The room this space is in.
|
||||||
|
/// </summary>
|
||||||
internal readonly Room room;
|
internal readonly Room room;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ namespace EscapeRoomEngine.Engine.Runtime
|
|||||||
public GameObject prefab;
|
public GameObject prefab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A space tile contains all game objects necessary to create a space from 2m x 2m up to any size.
|
||||||
|
/// </summary>
|
||||||
public class SpaceTile : MonoBehaviour
|
public class SpaceTile : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static HashSet<TileLocation> EveryTileLocation => new(new[]
|
public static HashSet<TileLocation> EveryTileLocation => new(new[]
|
||||||
|
|||||||
@@ -13,12 +13,22 @@ namespace EscapeRoomEngine.Engine.Runtime.UI
|
|||||||
Stopped, Paused, Running
|
Stopped, Paused, Running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The component that manages the gamemaster UI and the time.
|
||||||
|
/// </summary>
|
||||||
public class GameControl : MonoBehaviour
|
public class GameControl : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The active instance of the game control.
|
||||||
|
/// </summary>
|
||||||
public static GameControl Instance { get; private set; }
|
public static GameControl Instance { get; private set; }
|
||||||
|
|
||||||
|
[Tooltip("How much time in seconds should be between UI updates.")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private float uiUpdateInterval = 1, planUpdateInterval = 1;
|
private float uiUpdateInterval = 1;
|
||||||
|
[Tooltip("How much time in seconds should be between puzzle plan updates.")]
|
||||||
|
[SerializeField]
|
||||||
|
private float planUpdateInterval = 1;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
private Button startButton, stopButton, pauseButton, addTimeButton, removeTimeButton;
|
private Button startButton, stopButton, pauseButton, addTimeButton, removeTimeButton;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
@@ -36,10 +46,25 @@ namespace EscapeRoomEngine.Engine.Runtime.UI
|
|||||||
set => puzzlePlan.Puzzles = value;
|
set => puzzlePlan.Puzzles = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time that elapsed since the game was started.
|
||||||
|
/// </summary>
|
||||||
public float TimeElapsed { get; private set; }
|
public float TimeElapsed { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The time the player has spent in this room.
|
||||||
|
/// </summary>
|
||||||
public float TimeInRoom { get; set; }
|
public float TimeInRoom { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The target time set by the game master.
|
||||||
|
/// </summary>
|
||||||
public float TargetTime { get; private set; }
|
public float TargetTime { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The estimated total time the player will have spent when they finish this room.
|
||||||
|
/// </summary>
|
||||||
public float EstimatedTimeRoom { get; private set; }
|
public float EstimatedTimeRoom { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The estimated total time the player will spend in the experience.
|
||||||
|
/// </summary>
|
||||||
public float EstimatedTime { get; private set; }
|
public float EstimatedTime { get; private set; }
|
||||||
|
|
||||||
private float _previousUIUpdate, _previousPlanUpdate;
|
private float _previousUIUpdate, _previousPlanUpdate;
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.UI
|
namespace EscapeRoomEngine.Engine.Runtime.UI
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This component is responsible to change the icon of the pause button depending on the game state.
|
||||||
|
/// </summary>
|
||||||
public class PauseButton : MonoBehaviour
|
public class PauseButton : MonoBehaviour
|
||||||
{
|
{
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ using System.Collections.Generic;
|
|||||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||||
using NaughtyAttributes;
|
using NaughtyAttributes;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.UI
|
namespace EscapeRoomEngine.Engine.Runtime.UI
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The puzzle plan UI component that displays the current puzzle plan in the game master window.
|
||||||
|
/// </summary>
|
||||||
public class PuzzlePlan : MonoBehaviour
|
public class PuzzlePlan : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The distance from one entry to the next.
|
||||||
|
/// </summary>
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private Vector2 entryOffset;
|
private Vector2 entryOffset;
|
||||||
[BoxGroup("Internal")] [SerializeField]
|
[BoxGroup("Internal")] [SerializeField]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using EscapeRoomEngine.Engine.Runtime.Measurements;
|
using EscapeRoomEngine.Engine.Runtime.Measurements;
|
||||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||||
@@ -8,6 +7,9 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.UI
|
namespace EscapeRoomEngine.Engine.Runtime.UI
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An entry in the <see cref="PuzzlePlan"/> UI component that displays the name and estimated time for a puzzle.
|
||||||
|
/// </summary>
|
||||||
public class PuzzlePlanEntry : MonoBehaviour
|
public class PuzzlePlanEntry : MonoBehaviour
|
||||||
{
|
{
|
||||||
[BoxGroup("Internal")] [Required] [SerializeField]
|
[BoxGroup("Internal")] [Required] [SerializeField]
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ using System;
|
|||||||
|
|
||||||
// ReSharper disable ReturnTypeCanBeEnumerable.Global
|
// ReSharper disable ReturnTypeCanBeEnumerable.Global
|
||||||
// ReSharper disable ParameterTypeCanBeEnumerable.Local
|
// ReSharper disable ParameterTypeCanBeEnumerable.Local
|
||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The backtrack algorithm can calculate the subset of a set of samples with the sum closest to a target value.
|
||||||
|
/// </summary>
|
||||||
public struct Backtrack
|
public struct Backtrack
|
||||||
{
|
{
|
||||||
private int[] indices;
|
private int[] indices;
|
||||||
@@ -95,6 +97,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
return Sum(leave) < Sum(pick) ? leave : pick;
|
return Sum(leave) < Sum(pick) ? leave : pick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combine the two brute force algorithms to calculate the optimal subset.
|
||||||
|
/// </summary>
|
||||||
public int[] BruteForce()
|
public int[] BruteForce()
|
||||||
{
|
{
|
||||||
var lower = BruteForceLower();
|
var lower = BruteForceLower();
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace EscapeRoomEngine.Desert.Runtime
|
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This component disables a certain game object from being selectable in the scene view.
|
||||||
|
/// </summary>
|
||||||
public class DisablePicking : MonoBehaviour
|
public class DisablePicking : MonoBehaviour
|
||||||
{
|
{
|
||||||
public GameObject objectToDisable;
|
public GameObject objectToDisable;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores both the hdr and ldr version of a colour.
|
||||||
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct DynamicColor
|
public struct DynamicColor
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,25 +19,24 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
|
|
||||||
public class Logger : MonoBehaviour
|
public class Logger : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static Logger DefaultLogger
|
/// <summary>
|
||||||
{
|
/// The active instance of the logger.
|
||||||
get
|
/// </summary>
|
||||||
{
|
private static Logger Instance { get; set; }
|
||||||
if (_foundLogger == null)
|
|
||||||
{
|
|
||||||
_foundLogger = FindObjectOfType<Logger>();
|
|
||||||
}
|
|
||||||
return _foundLogger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static Logger _foundLogger;
|
|
||||||
|
|
||||||
|
[Tooltip("Toggle logging on or off globally.")]
|
||||||
public bool loggingEnabled;
|
public bool loggingEnabled;
|
||||||
|
[Tooltip("This list determines what types of log messages should be displayed.")]
|
||||||
public List<LogType> typeFilter;
|
public List<LogType> typeFilter;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Log(string message, LogType type)
|
public static void Log(string message, LogType type)
|
||||||
{
|
{
|
||||||
if (DefaultLogger.loggingEnabled && DefaultLogger.typeFilter.Contains(type))
|
if (Instance.loggingEnabled && Instance.typeFilter.Contains(type))
|
||||||
{
|
{
|
||||||
Debug.Log($"<b>[{type}]</b> {message}");
|
Debug.Log($"<b>[{type}]</b> {message}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,25 @@ using Random = System.Random;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The representation of a normal distribution with a certain mean μ and standard deviation σ.
|
||||||
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct NormalDistribution
|
public struct NormalDistribution
|
||||||
{
|
{
|
||||||
public float μ, σ;
|
/// <summary>
|
||||||
|
/// The mean of this distribution.
|
||||||
|
/// </summary>
|
||||||
|
public float μ;
|
||||||
|
/// <summary>
|
||||||
|
/// The standard deviation of this distribution.
|
||||||
|
/// </summary>
|
||||||
|
public float σ;
|
||||||
|
|
||||||
public static NormalDistribution Standard => new NormalDistribution { μ = 0, σ = 1 };
|
/// <summary>
|
||||||
|
/// Generate a standard normal distribution.
|
||||||
|
/// </summary>
|
||||||
|
public static NormalDistribution Standard => new() { μ = 0, σ = 1 };
|
||||||
|
|
||||||
public NormalDistribution(float[] samples) : this()
|
public NormalDistribution(float[] samples) : this()
|
||||||
{
|
{
|
||||||
@@ -19,13 +32,25 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
σ = Probability.StandardDeviation(samples, μ);
|
σ = Probability.StandardDeviation(samples, μ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sample a random value from this distribution.
|
||||||
|
/// </summary>
|
||||||
public float Sample() => σ * Probability.Normal() + μ;
|
public float Sample() => σ * Probability.Normal() + μ;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sample the CDF of this distribution.
|
||||||
|
/// </summary>
|
||||||
public float Cumulative(float x) => (float)new Normal(μ, σ).CumulativeDistribution(x);
|
public float Cumulative(float x) => (float)new Normal(μ, σ).CumulativeDistribution(x);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sample the inverse CDF of this distribution.
|
||||||
|
/// </summary>
|
||||||
public float InverseCumulative(float x) => (float)new Normal(μ, σ).InverseCumulativeDistribution(x);
|
public float InverseCumulative(float x) => (float)new Normal(μ, σ).InverseCumulativeDistribution(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class is used for probability calculations.
|
||||||
|
/// </summary>
|
||||||
public static class Probability
|
public static class Probability
|
||||||
{
|
{
|
||||||
private static readonly Random _random = new();
|
private static readonly Random _random = new();
|
||||||
@@ -50,6 +75,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
return u1 * Mathf.Sqrt(-2 * Mathf.Log(square) / square);
|
return u1 * Mathf.Sqrt(-2 * Mathf.Log(square) / square);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the mean of a list of samples.
|
||||||
|
/// </summary>
|
||||||
public static float Mean(float[] samples)
|
public static float Mean(float[] samples)
|
||||||
{
|
{
|
||||||
if (samples.Length == 0)
|
if (samples.Length == 0)
|
||||||
@@ -60,7 +88,13 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
return samples.Sum() / samples.Length;
|
return samples.Sum() / samples.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the standard deviation of a list of samples.
|
||||||
|
/// </summary>
|
||||||
public static float StandardDeviation(float[] samples) => StandardDeviation(samples, Mean(samples));
|
public static float StandardDeviation(float[] samples) => StandardDeviation(samples, Mean(samples));
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the standard deviation of a list of samples without recalculating the mean.
|
||||||
|
/// </summary>
|
||||||
public static float StandardDeviation(float[] samples, float mean)
|
public static float StandardDeviation(float[] samples, float mean)
|
||||||
{
|
{
|
||||||
var deviations = new float[samples.Length];
|
var deviations = new float[samples.Length];
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This struct represents an integer range.
|
||||||
|
/// </summary>
|
||||||
public struct Range
|
public struct Range
|
||||||
{
|
{
|
||||||
public int min, max;
|
public int min, max;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length of the range, excluding the maximum value.
|
||||||
|
/// </summary>
|
||||||
public int Length => max - min;
|
public int Length => max - min;
|
||||||
|
|
||||||
public Range(int min, int max)
|
public Range(int min, int max)
|
||||||
@@ -12,6 +18,9 @@
|
|||||||
this.max = max;
|
this.max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an array of every value in this range.
|
||||||
|
/// </summary>
|
||||||
public int[] ToArray(bool includeMax = false)
|
public int[] ToArray(bool includeMax = false)
|
||||||
{
|
{
|
||||||
var count = includeMax ? Length + 1 : Length;
|
var count = includeMax ? Length + 1 : Length;
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
|
|
||||||
#region Randomness
|
#region Randomness
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a random value in a range, including the last value.
|
||||||
|
/// </summary>
|
||||||
public static int RandomInclusive(int from, int to) => Random.Range(from, to + 1);
|
public static int RandomInclusive(int from, int to) => Random.Range(from, to + 1);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -41,6 +44,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a random value from a list.
|
||||||
|
/// </summary>
|
||||||
public static T RandomElement<T>(this List<T> list) => list[Random.Range(0, list.Count)];
|
public static T RandomElement<T>(this List<T> list) => list[Random.Range(0, list.Count)];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -59,8 +65,14 @@ namespace EscapeRoomEngine.Engine.Runtime.Utilities
|
|||||||
|
|
||||||
public static class Vector2IntExtensions
|
public static class Vector2IntExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Project a 2D vector onto the floor in 3D space.
|
||||||
|
/// </summary>
|
||||||
public static Vector3Int ProjectAtFloor(this Vector2Int vector) => vector.ProjectAtHeight(0);
|
public static Vector3Int ProjectAtFloor(this Vector2Int vector) => vector.ProjectAtHeight(0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Project a 2D vector onto a specific height in 3D space.
|
||||||
|
/// </summary>
|
||||||
public static Vector3Int ProjectAtHeight(this Vector2Int vector, int height) =>
|
public static Vector3Int ProjectAtHeight(this Vector2Int vector, int height) =>
|
||||||
new Vector3Int(vector.x, height, vector.y);
|
new Vector3Int(vector.x, height, vector.y);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Portal.Runtime
|
namespace EscapeRoomEngine.Portal.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The portal is a specific type of <see cref="DoorState"/> for seamless transitions between spaces.
|
||||||
|
/// </summary>
|
||||||
public class Portal : DoorState
|
public class Portal : DoorState
|
||||||
{
|
{
|
||||||
public static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0));
|
public static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0));
|
||||||
@@ -24,6 +27,9 @@ namespace EscapeRoomEngine.Portal.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[BoxGroup("Internal")] public Transform portalTransform;
|
[BoxGroup("Internal")] public Transform portalTransform;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this portal is connected is determined by whether the reference to its linked portal is set.
|
||||||
|
/// </summary>
|
||||||
internal bool Connected => linkedPortal != null;
|
internal bool Connected => linkedPortal != null;
|
||||||
internal readonly List<PortalDriver> closePortalDrivers = new();
|
internal readonly List<PortalDriver> closePortalDrivers = new();
|
||||||
|
|
||||||
@@ -70,6 +76,9 @@ namespace EscapeRoomEngine.Portal.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin tracking a portal driver that came close to this portal and might need to be teleported.
|
||||||
|
/// </summary>
|
||||||
internal void StartTrackingDriver(PortalDriver portalDriver, int entrySide)
|
internal void StartTrackingDriver(PortalDriver portalDriver, int entrySide)
|
||||||
{
|
{
|
||||||
closePortalDrivers.Add(portalDriver);
|
closePortalDrivers.Add(portalDriver);
|
||||||
@@ -77,12 +86,18 @@ namespace EscapeRoomEngine.Portal.Runtime
|
|||||||
portalDriver.entrySide = entrySide;
|
portalDriver.entrySide = entrySide;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End tracking a portal driver that distanced itself from this portal or was teleported to the linked portal.
|
||||||
|
/// </summary>
|
||||||
internal void StopTrackingDriver(PortalDriver portalDriver)
|
internal void StopTrackingDriver(PortalDriver portalDriver)
|
||||||
{
|
{
|
||||||
closePortalDrivers.Remove(portalDriver);
|
closePortalDrivers.Remove(portalDriver);
|
||||||
portalDriver.DisableClone(linkedPortal);
|
portalDriver.DisableClone(linkedPortal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate which side of the portal plane a transform is.
|
||||||
|
/// </summary>
|
||||||
internal int CalculateSide(Transform portalDriverTransform)
|
internal int CalculateSide(Transform portalDriverTransform)
|
||||||
{
|
{
|
||||||
return Math.Sign(Vector3.Dot(portalTransform.forward, portalDriverTransform.position - portalTransform.position));
|
return Math.Sign(Vector3.Dot(portalTransform.forward, portalDriverTransform.position - portalTransform.position));
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace EscapeRoomEngine.Portal.Runtime
|
|||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
// get player camera
|
// get player camera
|
||||||
_player = Player.Current;
|
_player = Player.Instance;
|
||||||
|
|
||||||
// get portal camera
|
// get portal camera
|
||||||
_camera = GetComponent<Camera>();
|
_camera = GetComponent<Camera>();
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Portal.Runtime
|
namespace EscapeRoomEngine.Portal.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The portal collider is responsible for detecting <see cref="PortalDriver"/>s that come close to this portal.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Collider))]
|
[RequireComponent(typeof(Collider))]
|
||||||
public class PortalCollider : MonoBehaviour
|
public class PortalCollider : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Portal.Runtime
|
namespace EscapeRoomEngine.Portal.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A portal driver is any object that can be teleported by a portal like the player, their hands or objects.
|
||||||
|
/// </summary>
|
||||||
[RequireComponent(typeof(Collider), typeof(Rigidbody))]
|
[RequireComponent(typeof(Collider), typeof(Rigidbody))]
|
||||||
public class PortalDriver : MonoBehaviour
|
public class PortalDriver : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using UnityEngine.XR.Interaction.Toolkit;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.Portal.Runtime
|
namespace EscapeRoomEngine.Portal.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The clone of a <see cref="PortalDriver"/> is a copy of it that was stripped of most of its components. Its position is updated by the driver.
|
||||||
|
/// </summary>
|
||||||
public class PortalDriverClone : MonoBehaviour
|
public class PortalDriverClone : MonoBehaviour
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Test_Assets
|
namespace Test_Assets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is used to test the <see cref="Backtrack"/> struct.
|
||||||
|
/// </summary>
|
||||||
public class BacktrackingTest : MonoBehaviour
|
public class BacktrackingTest : MonoBehaviour
|
||||||
{
|
{
|
||||||
public int[] values;
|
public int[] values;
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Test_Assets
|
namespace Test_Assets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is used to test the <<see cref="Probability"/> class.
|
||||||
|
/// </summary>
|
||||||
public class ProbabilityTest : MonoBehaviour
|
public class ProbabilityTest : MonoBehaviour
|
||||||
{
|
{
|
||||||
public NormalDistribution distribution = NormalDistribution.Standard;
|
public NormalDistribution distribution = NormalDistribution.Standard;
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace EscapeRoomEngine.VR.Runtime
|
namespace EscapeRoomEngine.VR.Runtime
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The player component contains references to the main camera, eye positions and hand positions of the VR player.
|
||||||
|
/// </summary>
|
||||||
public class Player : MonoBehaviour
|
public class Player : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static Player Current
|
/// <summary>
|
||||||
|
/// The active player instance.
|
||||||
|
/// </summary>
|
||||||
|
public static Player Instance { get; private set; }
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
{
|
{
|
||||||
get
|
Instance = this;
|
||||||
{
|
|
||||||
if (_foundPlayer == null)
|
|
||||||
{
|
|
||||||
_foundPlayer = FindObjectOfType<Player>();
|
|
||||||
}
|
|
||||||
return _foundPlayer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private static Player _foundPlayer;
|
|
||||||
|
|
||||||
[BoxGroup("Internal")] public new Camera camera;
|
[BoxGroup("Internal")] public new Camera camera;
|
||||||
[BoxGroup("Internal")] [SerializeField] private Transform leftEye, rightEye;
|
[BoxGroup("Internal")] [SerializeField] private Transform leftEye, rightEye;
|
||||||
|
|||||||
Reference in New Issue
Block a user