small fixes
This commit is contained in:
124
Assets/Station46/Runtime/Button.cs
Normal file
124
Assets/Station46/Runtime/Button.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime
|
||||
{
|
||||
public enum ButtonEventType
|
||||
{
|
||||
Pressed, Released, Activated, Deactivated
|
||||
}
|
||||
|
||||
public static class ButtonEventExtensions
|
||||
{
|
||||
public static string Description(this ButtonEventType type, Button button)
|
||||
{
|
||||
var action = type switch
|
||||
{
|
||||
ButtonEventType.Pressed => "pressed",
|
||||
ButtonEventType.Released => "released",
|
||||
ButtonEventType.Activated => "activated",
|
||||
ButtonEventType.Deactivated => "deactivated",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
|
||||
return $"{button} was {action}";
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ButtonEventHandler(Button source, ButtonEventType e);
|
||||
|
||||
/// <summary>
|
||||
/// A general component for buttons that handles button events.
|
||||
/// </summary>
|
||||
public class Button : MonoBehaviour
|
||||
{
|
||||
public event ButtonEventHandler ButtonEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this button accepts input.
|
||||
/// </summary>
|
||||
[ShowNativeProperty]
|
||||
protected bool Active
|
||||
{
|
||||
get => _active;
|
||||
set
|
||||
{
|
||||
var previous = _active;
|
||||
_active = value;
|
||||
|
||||
if (previous != _active)
|
||||
{
|
||||
OnButtonEvent(_active ? ButtonEventType.Activated : ButtonEventType.Deactivated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this button is currently pressed.
|
||||
/// </summary>
|
||||
[ShowNativeProperty]
|
||||
protected bool Pressed
|
||||
{
|
||||
get => _pressed;
|
||||
set
|
||||
{
|
||||
var previous = _pressed;
|
||||
_pressed = Active && value;
|
||||
|
||||
if (previous != _pressed)
|
||||
{
|
||||
OnButtonEvent(_pressed ? ButtonEventType.Pressed : ButtonEventType.Released);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _active = true, _pressed;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
ButtonEvent += (_, type) =>
|
||||
{
|
||||
if (type == ButtonEventType.Deactivated)
|
||||
{
|
||||
Pressed = false; // release button if it is deactivated
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void OnButtonEvent(ButtonEventType type)
|
||||
{
|
||||
Logger.Log(type.Description(this), LogType.PuzzleDetail);
|
||||
|
||||
ButtonEvent?.Invoke(this, type);
|
||||
}
|
||||
|
||||
#region Debug Buttons
|
||||
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
public void Enable() => Active = true;
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
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)]
|
||||
public void PressAndRelease()
|
||||
{
|
||||
Press();
|
||||
Release();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Button.cs.meta
Normal file
3
Assets/Station46/Runtime/Button.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0388274fd30c44089477f6bed40e1e23
|
||||
timeCreated: 1668813709
|
||||
48
Assets/Station46/Runtime/Crystal.cs
Normal file
48
Assets/Station46/Runtime/Crystal.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// A rotating crystal that can change colour.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Emission))]
|
||||
public class Crystal : MonoBehaviour
|
||||
{
|
||||
[Required]
|
||||
[BoxGroup("Internal")]
|
||||
public Light crystalLight;
|
||||
|
||||
/// <summary>
|
||||
/// Turns the crystal light on or off.
|
||||
/// </summary>
|
||||
public bool Active
|
||||
{
|
||||
set
|
||||
{
|
||||
_emission.active = value;
|
||||
crystalLight.enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The crystal light and emission colours.
|
||||
/// </summary>
|
||||
public DynamicColor Color
|
||||
{
|
||||
set
|
||||
{
|
||||
_emission.color = value.hdr;
|
||||
crystalLight.color = value.ldr;
|
||||
}
|
||||
}
|
||||
|
||||
private Emission _emission;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_emission = GetComponent<Emission>();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Crystal.cs.meta
Normal file
3
Assets/Station46/Runtime/Crystal.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b9480c87b3b4def9c85adb088dab147
|
||||
timeCreated: 1668703088
|
||||
18
Assets/Station46/Runtime/Desert.asmdef
Normal file
18
Assets/Station46/Runtime/Desert.asmdef
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Desert",
|
||||
"rootNamespace": "EscapeRoomEngine",
|
||||
"references": [
|
||||
"GUID:2d68e204354e44f2a2ecf3cfa9213c5f",
|
||||
"GUID:ba4c7dba98ca4c31818cc46276b5dea1",
|
||||
"GUID:776d03a35f1b52c4a9aed9f56d7b4229"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
3
Assets/Station46/Runtime/Desert.asmdef.meta
Normal file
3
Assets/Station46/Runtime/Desert.asmdef.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20d0c4bd521546c7859e37019a165e38
|
||||
timeCreated: 1668940232
|
||||
8
Assets/Station46/Runtime/Dispenser.meta
Normal file
8
Assets/Station46/Runtime/Dispenser.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 073fb5180fa20564bb163c47ccafc413
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
103
Assets/Station46/Runtime/Dispenser/Dispenser.cs
Normal file
103
Assets/Station46/Runtime/Dispenser/Dispenser.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
||||
{
|
||||
/// <summary>
|
||||
/// The main component for the dispenser module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Animator), typeof(Emission))]
|
||||
public class Dispenser : ModuleState
|
||||
{
|
||||
private static readonly int Open = Animator.StringToHash("Open");
|
||||
|
||||
[Tooltip("The object this dispenser should produce.")]
|
||||
public Transform dispensable;
|
||||
[Tooltip("The time in seconds to wait until another object can be produced.")]
|
||||
[SerializeField] [Min(0)]
|
||||
private float cooldown;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Transform dispenseOrigin;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Button dispenseButton;
|
||||
|
||||
/// <summary>
|
||||
/// The dispenser does not produce any more objects when it is solved and changes colour.
|
||||
/// </summary>
|
||||
public bool Solved
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
CloseHatch();
|
||||
dispenseButton.Disable();
|
||||
_light.color = Engine.Runtime.Engine.Theme.solvedColor.hdr;
|
||||
}
|
||||
else
|
||||
{
|
||||
dispenseButton.Enable();
|
||||
_light.color = Engine.Runtime.Engine.Theme.puzzleColor.hdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float _previousDispense = -1;
|
||||
private Animator _animator;
|
||||
private Emission _light;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
_light = GetComponent<Emission>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
dispenseButton.ButtonEvent += (_, type) =>
|
||||
{
|
||||
if (type == ButtonEventType.Pressed)
|
||||
{
|
||||
Dispense();
|
||||
}
|
||||
};
|
||||
|
||||
_light.active = true;
|
||||
}
|
||||
|
||||
#region Debug Buttons
|
||||
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
public void Dispense()
|
||||
{
|
||||
if (_previousDispense + cooldown <= Time.time)
|
||||
{
|
||||
Logger.Log($"{this} dispensing {dispensable.gameObject}", LogType.PuzzleDetail);
|
||||
|
||||
_animator.SetBool(Open, true);
|
||||
Instantiate(dispensable, dispenseOrigin.position, dispenseOrigin.rotation);
|
||||
|
||||
_previousDispense = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
public void CloseHatch()
|
||||
{
|
||||
_animator.SetBool(Open, false);
|
||||
}
|
||||
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
public void Solve() => Solved = true;
|
||||
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
public void Reset() => Solved = false;
|
||||
|
||||
#endregion
|
||||
|
||||
public override void SetModule(Module module) {}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Dispenser/Dispenser.cs.meta
Normal file
3
Assets/Station46/Runtime/Dispenser/Dispenser.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ca2acc9138242c59fbba68a62435318
|
||||
timeCreated: 1669991155
|
||||
42
Assets/Station46/Runtime/Dispenser/DispenserLights.cs
Normal file
42
Assets/Station46/Runtime/Dispenser/DispenserLights.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
||||
{
|
||||
[Serializable]
|
||||
public struct DispenserLightRow
|
||||
{
|
||||
public bool[] lights;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component controls the grid of lights on the dispenser.
|
||||
/// </summary>
|
||||
public class DispenserLights : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Control the pattern of lights on a dispenser.")]
|
||||
[SerializeField]
|
||||
private DispenserLightRow[] lights;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private GameObject lightPrefab;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Vector2Int gridDimensions;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Vector2 lightOffset;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
for (var y = 0; y < gridDimensions.y; y++)
|
||||
{
|
||||
var row = lights[y].lights;
|
||||
for (var x = 0; x < gridDimensions.x; x++)
|
||||
{
|
||||
var instance = Instantiate(lightPrefab, transform, false);
|
||||
instance.transform.localPosition = new Vector3(-x * lightOffset.x, 0, y * lightOffset.y);
|
||||
instance.GetComponent<Emission>().active = row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b796efe5676490b81fe24366d556b18
|
||||
timeCreated: 1670273719
|
||||
25
Assets/Station46/Runtime/Dispenser/DispenserOrb.cs
Normal file
25
Assets/Station46/Runtime/Dispenser/DispenserOrb.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Dispenser
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody), typeof(Collider), typeof(Emission))]
|
||||
public class DispenserOrb : MonoBehaviour
|
||||
{
|
||||
public Color Color
|
||||
{
|
||||
set => _emission.color = value;
|
||||
}
|
||||
|
||||
private Emission _emission;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_emission = GetComponent<Emission>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_emission.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Dispenser/DispenserOrb.cs.meta
Normal file
3
Assets/Station46/Runtime/Dispenser/DispenserOrb.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 461335b38aec4817a787dae9c5a63f0b
|
||||
timeCreated: 1669815545
|
||||
71
Assets/Station46/Runtime/Emission.cs
Normal file
71
Assets/Station46/Runtime/Emission.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// A general component for controlling the emission of an object.
|
||||
/// </summary>
|
||||
public class Emission : MonoBehaviour
|
||||
{
|
||||
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;
|
||||
|
||||
private bool _previousActive;
|
||||
private Color _previousColor;
|
||||
private Material _material;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_material = emissionRenderer.material;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ChangedToggle();
|
||||
ChangedColor();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_previousActive != active)
|
||||
{
|
||||
ChangedToggle();
|
||||
_previousActive = active;
|
||||
}
|
||||
|
||||
if (!_previousColor.Equals(color))
|
||||
{
|
||||
ChangedColor();
|
||||
_previousColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangedToggle()
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
_material.EnableKeyword("_EMISSION");
|
||||
}
|
||||
else
|
||||
{
|
||||
_material.DisableKeyword("_EMISSION");
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangedColor()
|
||||
{
|
||||
_material.SetColor(EmissionColorNameID, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Emission.cs.meta
Normal file
3
Assets/Station46/Runtime/Emission.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17de1e2991b64847bceea06f966f0560
|
||||
timeCreated: 1668704065
|
||||
43
Assets/Station46/Runtime/HoloButton.cs
Normal file
43
Assets/Station46/Runtime/HoloButton.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// A holographic <see cref="Button"/> that changes its colour when it is pressed.
|
||||
/// </summary>
|
||||
public class HoloButton : Button
|
||||
{
|
||||
private static readonly int FresnelColor = Shader.PropertyToID("_FresnelColor");
|
||||
private static readonly int Color = Shader.PropertyToID("_Color");
|
||||
|
||||
[BoxGroup("Internal")] [Required] public MeshRenderer holoRenderer;
|
||||
|
||||
private Material _material;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_material = holoRenderer.material;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
ButtonEvent += (_, _) =>
|
||||
{
|
||||
var color = Engine.Runtime.Engine.Theme.puzzleColor;
|
||||
if (!Active)
|
||||
{
|
||||
color = Engine.Runtime.Engine.Theme.solvedColor;
|
||||
} else if (Pressed)
|
||||
{
|
||||
color = Engine.Runtime.Engine.Theme.activeColor;
|
||||
}
|
||||
|
||||
_material.SetColor(FresnelColor, color.hdr);
|
||||
_material.SetColor(Color, color.hdr);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/HoloButton.cs.meta
Normal file
3
Assets/Station46/Runtime/HoloButton.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 352860bb2c6549d0b3e84104afef3f54
|
||||
timeCreated: 1670019940
|
||||
103
Assets/Station46/Runtime/Hoop.cs
Normal file
103
Assets/Station46/Runtime/Hoop.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using EscapeRoomEngine.Desert.Runtime.Dispenser;
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// The main component for the orb throwing module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
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;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private List<Emission> rings;
|
||||
|
||||
private readonly HashSet<GameObject> _orbs = new();
|
||||
private Dispenser.Dispenser _dispenser;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
PuzzleEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (type)
|
||||
{
|
||||
case PuzzleEventType.Restarted:
|
||||
var color = Engine.Runtime.Engine.Theme.puzzleColor;
|
||||
rings.ForEach(ring => ring.color = color.hdr);
|
||||
_dispenser.Reset();
|
||||
break;
|
||||
case PuzzleEventType.Solved:
|
||||
color = Engine.Runtime.Engine.Theme.solvedColor;
|
||||
rings.ForEach(ring => ring.color = color.hdr);
|
||||
_dispenser.Solve();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void SetState(int index, int value, bool checkSolution)
|
||||
{
|
||||
base.SetState(index, value, checkSolution);
|
||||
|
||||
for (var i = 0; i < rings.Count; i++)
|
||||
{
|
||||
rings[i].active = i < value;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator SetOrbCount()
|
||||
{
|
||||
if (!Solved)
|
||||
{
|
||||
yield return new WaitForSeconds(updateDelay);
|
||||
SetState(0, _orbs.Count, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
var orb = other.GetComponent<DispenserOrb>();
|
||||
if (orb)
|
||||
{
|
||||
_orbs.Add(orb.gameObject);
|
||||
StartCoroutine(SetOrbCount());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
var orb = other.GetComponent<DispenserOrb>();
|
||||
if (orb)
|
||||
{
|
||||
_orbs.Remove(orb.gameObject);
|
||||
StartCoroutine(SetOrbCount());
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetModule(Module module)
|
||||
{
|
||||
base.SetModule(module);
|
||||
|
||||
// The hoop requires a related dispenser module
|
||||
var firstRelatedModule = Module.relatedModules[0];
|
||||
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
||||
{
|
||||
_dispenser = dispenser;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EngineException("Hoop was not assigned a related Dispenser.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Hoop.cs.meta
Normal file
3
Assets/Station46/Runtime/Hoop.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 175342625aa94174a12590b46ad484fc
|
||||
timeCreated: 1670520604
|
||||
3
Assets/Station46/Runtime/Portal.meta
Normal file
3
Assets/Station46/Runtime/Portal.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 788a501fd6ac4d74bc34a49c99b02fc2
|
||||
timeCreated: 1670792890
|
||||
37
Assets/Station46/Runtime/Portal/Station46Portal.cs
Normal file
37
Assets/Station46/Runtime/Portal/Station46Portal.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Station46.Runtime.Portal
|
||||
{
|
||||
/// <summary>
|
||||
/// The desert theme includes its own version of a portal that changes colour when it is unlocked.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Emission))]
|
||||
public class DesertPortal : EscapeRoomEngine.Portal.Runtime.Portal
|
||||
{
|
||||
private Emission _emission;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_emission = GetComponent<Emission>();
|
||||
|
||||
DoorEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault ConvertSwitchStatementToSwitchExpression
|
||||
switch (type)
|
||||
{
|
||||
case DoorEventType.Unlocked:
|
||||
_emission.color = Engine.Runtime.Engine.Theme.solvedColor.hdr;
|
||||
break;
|
||||
case DoorEventType.Locked:
|
||||
_emission.color = Engine.Runtime.Engine.Theme.puzzleColor.hdr;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
_emission.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Portal/Station46Portal.cs.meta
Normal file
3
Assets/Station46/Runtime/Portal/Station46Portal.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20837d5efd544d3eb07657f581cb76d2
|
||||
timeCreated: 1670792901
|
||||
8
Assets/Station46/Runtime/Puzzle A.meta
Normal file
8
Assets/Station46/Runtime/Puzzle A.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6aa1e96d6564ba419a84d5bcb635568
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/Station46/Runtime/Puzzle A/Ball.cs
Normal file
39
Assets/Station46/Runtime/Puzzle A/Ball.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using NaughtyAttributes;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||
{
|
||||
/// <summary>
|
||||
/// The main component for the symbol ball module.
|
||||
/// </summary>
|
||||
public class Ball : ModuleState
|
||||
{
|
||||
[BoxGroup("Internal")] [Required]
|
||||
public Emission statusLight;
|
||||
[BoxGroup("Internal")] [Required]
|
||||
public Ring ring;
|
||||
|
||||
public bool StatusLight
|
||||
{
|
||||
set => statusLight.active = value;
|
||||
}
|
||||
|
||||
public bool Active
|
||||
{
|
||||
set
|
||||
{
|
||||
statusLight.color = value ?
|
||||
Engine.Runtime.Engine.Theme.puzzleColor.hdr
|
||||
: Engine.Runtime.Engine.Theme.solvedColor.hdr;
|
||||
ring.rotating = value;
|
||||
if (!value)
|
||||
{
|
||||
ring.crystal.Active = false;
|
||||
ring.symbols.ForEach(symbol => symbol.Active = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetModule(Module module) {}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Puzzle A/Ball.cs.meta
Normal file
3
Assets/Station46/Runtime/Puzzle A/Ball.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9b2d31478f94735a2e19e7c07a27f05
|
||||
timeCreated: 1668705636
|
||||
65
Assets/Station46/Runtime/Puzzle A/Ring.cs
Normal file
65
Assets/Station46/Runtime/Puzzle A/Ring.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||
using JetBrains.Annotations;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||
{
|
||||
/// <summary>
|
||||
/// The rotating ring in the symbol ball module.
|
||||
/// </summary>
|
||||
public class Ring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Whether the ring is rotating.")]
|
||||
public bool rotating = true;
|
||||
[Tooltip("The current angle of the rotation.")]
|
||||
public float rotationAngle;
|
||||
[Tooltip("The range in degrees from the front of the module where a symbol should be lit up.")]
|
||||
[MinMaxSlider(-180, 180)]
|
||||
public Vector2 activeRange;
|
||||
[BoxGroup("Internal")] [Required]
|
||||
public Crystal crystal;
|
||||
[BoxGroup("Internal")]
|
||||
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
||||
public List<Symbol> symbols;
|
||||
[BoxGroup("Internal")]
|
||||
public List<Transform> symbolSlots;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
var angleDistance = 360f / symbols.Count;
|
||||
List<Symbol> symbolInstances = new(symbols.Count);
|
||||
|
||||
for (var i = 0; i < symbols.Count; i++)
|
||||
{
|
||||
var symbol = Instantiate(symbols.RandomElement(), symbolSlots[i], false);
|
||||
symbol.symbolPosition = i;
|
||||
symbol.anglePosition = i * angleDistance;
|
||||
symbolInstances.Add(symbol);
|
||||
}
|
||||
|
||||
symbols = symbolInstances;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if(rotating)
|
||||
{
|
||||
transform.localRotation = Quaternion.AngleAxis(rotationAngle, Vector3.forward);
|
||||
|
||||
var activeSymbol = false;
|
||||
symbols.ForEach(symbol =>
|
||||
{
|
||||
var angle = (rotationAngle - symbol.anglePosition) % 360;
|
||||
var active = angle > activeRange.x && angle < activeRange.y || angle > 360 + activeRange.x;
|
||||
symbol.Active = active;
|
||||
activeSymbol |= active;
|
||||
});
|
||||
crystal.Active = activeSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly] private bool SymbolCount(List<Symbol> list) => list.Count == symbolSlots.Count;
|
||||
}
|
||||
}
|
||||
11
Assets/Station46/Runtime/Puzzle A/Ring.cs.meta
Normal file
11
Assets/Station46/Runtime/Puzzle A/Ring.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7902f6a7fa0fd844f8ed93e3debd7778
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
86
Assets/Station46/Runtime/Puzzle A/Symbol.cs
Normal file
86
Assets/Station46/Runtime/Puzzle A/Symbol.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||
{
|
||||
public enum SymbolEventType
|
||||
{
|
||||
Activated, Deactivated
|
||||
}
|
||||
|
||||
public static class SymbolEventExtensions
|
||||
{
|
||||
public static string Description(this SymbolEventType type, Symbol symbol)
|
||||
{
|
||||
var action = type switch
|
||||
{
|
||||
SymbolEventType.Activated => "activated",
|
||||
SymbolEventType.Deactivated => "deactivated",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
|
||||
return $"{symbol} was {action}";
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void SymbolEventHandler(SymbolEventType e);
|
||||
|
||||
/// <summary>
|
||||
/// The main component for the symbols on the symbol ring module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Emission))]
|
||||
public class Symbol : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The number of the symbol in the puzzle solution.")]
|
||||
public int symbolNumber;
|
||||
[BoxGroup("Internal")]
|
||||
public int symbolPosition;
|
||||
[BoxGroup("Internal")]
|
||||
public float anglePosition;
|
||||
public event SymbolEventHandler SymbolEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the symbol is lit up. This is controlled by the <see cref="Ring"/>.
|
||||
/// </summary>
|
||||
public bool Active
|
||||
{
|
||||
set
|
||||
{
|
||||
var previous = _emission.active;
|
||||
_emission.active = value;
|
||||
|
||||
if (previous != value)
|
||||
{
|
||||
OnSymbolEvent(value ? SymbolEventType.Activated : SymbolEventType.Deactivated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Emission _emission;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_emission = GetComponent<Emission>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Active = false;
|
||||
}
|
||||
|
||||
private void OnSymbolEvent(SymbolEventType type)
|
||||
{
|
||||
Logger.Log(type.Description(this), LogType.PuzzleDetail);
|
||||
|
||||
SymbolEvent?.Invoke(type);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Station46/Runtime/Puzzle A/Symbol.cs.meta
Normal file
11
Assets/Station46/Runtime/Puzzle A/Symbol.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e099fd852792c34188dcf102aa895e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/Station46/Runtime/Puzzle A/SymbolButton.cs
Normal file
13
Assets/Station46/Runtime/Puzzle A/SymbolButton.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
{
|
||||
[Tooltip("The number of the symbol assigned to this button.")]
|
||||
public int symbolNumber;
|
||||
}
|
||||
}
|
||||
11
Assets/Station46/Runtime/Puzzle A/SymbolButton.cs.meta
Normal file
11
Assets/Station46/Runtime/Puzzle A/SymbolButton.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6620bee0f14224b11a19808d537cbd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
154
Assets/Station46/Runtime/Puzzle A/Terminal.cs
Normal file
154
Assets/Station46/Runtime/Puzzle A/Terminal.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||
using JetBrains.Annotations;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
|
||||
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
|
||||
{
|
||||
/// <summary>
|
||||
/// The main component for the terminal module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class Terminal : CyclicStepPuzzle
|
||||
{
|
||||
private static readonly int LightFlash = Animator.StringToHash("Light Flash");
|
||||
|
||||
[BoxGroup("Internal")] [Required]
|
||||
public Emission terminalLight;
|
||||
[BoxGroup("Internal")]
|
||||
[ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
|
||||
public List<SymbolButton> symbols;
|
||||
[BoxGroup("Internal")]
|
||||
public List<Transform> symbolSlots;
|
||||
|
||||
private IOption<Symbol> _activeSymbol;
|
||||
private Animator _animator;
|
||||
private Ball _ball;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
|
||||
List<SymbolButton> symbolInstances = new(symbols.Count);
|
||||
symbolInstances.AddRange(symbols.Select((t, i) => Instantiate(t, symbolSlots[i], false)));
|
||||
symbols = symbolInstances;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
PuzzleEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
|
||||
switch (type)
|
||||
{
|
||||
case PuzzleEventType.Restarted:
|
||||
_ball.Active = true;
|
||||
terminalLight.color = Engine.Runtime.Engine.Theme.puzzleColor.hdr;
|
||||
symbols.ForEach(symbol => symbol.Enable());
|
||||
break;
|
||||
case PuzzleEventType.Solved:
|
||||
_ball.Active = false;
|
||||
terminalLight.color = Engine.Runtime.Engine.Theme.solvedColor.hdr;
|
||||
symbols.ForEach(symbol => symbol.Disable());
|
||||
break;
|
||||
case PuzzleEventType.WrongInput:
|
||||
_animator.SetTrigger(LightFlash);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
symbols.ForEach(symbol =>
|
||||
{
|
||||
symbol.ButtonEvent += (_, type) =>
|
||||
{
|
||||
if (type == ButtonEventType.Pressed)
|
||||
{
|
||||
PressedSymbol(symbol.symbolNumber);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_ball.ring.symbols.ForEach(symbol =>
|
||||
{
|
||||
symbol.SymbolEvent += type =>
|
||||
_activeSymbol = type == SymbolEventType.Activated ? Some<Symbol>.Of(symbol) : None<Symbol>.New();
|
||||
});
|
||||
}
|
||||
|
||||
public override void SetModule(Module module)
|
||||
{
|
||||
base.SetModule(module);
|
||||
|
||||
// The terminal requires a related symbol ball module
|
||||
var firstRelatedModule = Module.relatedModules[0];
|
||||
if (firstRelatedModule.State is Ball ball)
|
||||
{
|
||||
_ball = ball;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EngineException("Terminal was not assigned a related Ball.");
|
||||
}
|
||||
|
||||
TurnOnLights();
|
||||
}
|
||||
|
||||
[UsedImplicitly] // required in animation
|
||||
internal void TurnOnLights()
|
||||
{
|
||||
terminalLight.active = true;
|
||||
_ball.StatusLight = true;
|
||||
}
|
||||
|
||||
[UsedImplicitly] // required in animation
|
||||
internal void TurnOffLights()
|
||||
{
|
||||
terminalLight.active = false;
|
||||
_ball.StatusLight = false;
|
||||
}
|
||||
|
||||
private void PressedSymbol(int number)
|
||||
{
|
||||
if (!Solved)
|
||||
{
|
||||
Logger.Log($"Pressed symbol {number} on {this}", LogType.PuzzleDetail);
|
||||
|
||||
_activeSymbol.Match(some: symbol =>
|
||||
{
|
||||
if (number == symbol.symbolNumber)
|
||||
{
|
||||
CheckStep(symbol.symbolPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
WrongInput();
|
||||
}
|
||||
}, none: WrongInput);
|
||||
}
|
||||
}
|
||||
|
||||
#region Debug Buttons
|
||||
|
||||
[UsedImplicitly]
|
||||
[Button(enabledMode: EButtonEnableMode.Playmode)]
|
||||
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
|
||||
|
||||
[UsedImplicitly] // used for field validation
|
||||
private bool SymbolCount(List<SymbolButton> list) => list.Count == symbolSlots.Count;
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Puzzle A/Terminal.cs.meta
Normal file
3
Assets/Station46/Runtime/Puzzle A/Terminal.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c5df397607f423a8cde06954f22a2bd
|
||||
timeCreated: 1668762976
|
||||
3
Assets/Station46/Runtime/Puzzle B.meta
Normal file
3
Assets/Station46/Runtime/Puzzle B.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d97e612b38c148c1a988eefa9af29b75
|
||||
timeCreated: 1669687251
|
||||
112
Assets/Station46/Runtime/Puzzle B/PuzzleB.cs
Normal file
112
Assets/Station46/Runtime/Puzzle B/PuzzleB.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using EscapeRoomEngine.Desert.Runtime.Puzzle_A;
|
||||
using EscapeRoomEngine.Engine.Runtime;
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using JetBrains.Annotations;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
||||
{
|
||||
[Serializable]
|
||||
internal struct ButtonAction
|
||||
{
|
||||
public SymbolButton button;
|
||||
public List<int> stateIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main component for the hexagon module.
|
||||
/// </summary>
|
||||
public class PuzzleB : StatePuzzle
|
||||
{
|
||||
[Tooltip("How many seconds the buttons should be disabled until another input is accepted.")]
|
||||
[SerializeField] [Min(0)]
|
||||
private float buttonCooldown;
|
||||
[ValidateInput("CorrectRotatorCount")]
|
||||
public List<Rotator> rotators;
|
||||
[InfoBox("Every button action will toggle the states at the given indices between 0 and 90 degrees.")]
|
||||
[SerializeField]
|
||||
private List<ButtonAction> buttonActions;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private List<Emission> lights;
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Crystal crystal;
|
||||
|
||||
private float _previousPress = -1;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
PuzzleEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (type)
|
||||
{
|
||||
case PuzzleEventType.Restarted:
|
||||
var color = Engine.Runtime.Engine.Theme.puzzleColor;
|
||||
crystal.Color = color;
|
||||
lights.ForEach(emission => emission.color = color.hdr);
|
||||
rotators.ForEach(rotator => rotator.Emission.color = color.hdr);
|
||||
buttonActions.ForEach(action => action.button.Enable());
|
||||
break;
|
||||
case PuzzleEventType.Solved:
|
||||
color = Engine.Runtime.Engine.Theme.solvedColor;
|
||||
crystal.Color = color;
|
||||
lights.ForEach(emission => emission.color = color.hdr);
|
||||
rotators.ForEach(rotator => rotator.Emission.color = color.hdr);
|
||||
buttonActions.ForEach(action => action.button.Disable());
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var buttonAction in buttonActions)
|
||||
{
|
||||
buttonAction.button.ButtonEvent += (_, type) =>
|
||||
{
|
||||
if (type == ButtonEventType.Pressed && _previousPress + buttonCooldown <= Time.time)
|
||||
{
|
||||
Switch(buttonAction);
|
||||
|
||||
_previousPress = Time.time;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
crystal.Active = true;
|
||||
lights.ForEach(emission => emission.active = true);
|
||||
rotators.ForEach(rotator => rotator.Emission.active = true);
|
||||
}
|
||||
|
||||
protected override void SetState(int index, int value, bool checkSolution)
|
||||
{
|
||||
base.SetState(index, value, checkSolution);
|
||||
|
||||
for (var i = 0; i < stateCount; i++)
|
||||
{
|
||||
rotators[i].angle = states[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch all rotators specified by a given action.
|
||||
/// </summary>
|
||||
private void Switch(ButtonAction action)
|
||||
{
|
||||
for (var i = 0; i < action.stateIndices.Count; i++)
|
||||
{
|
||||
var stateIndex = action.stateIndices[i];
|
||||
var current = OrientationExtensions.FromAngle(states[stateIndex]);
|
||||
current = current == Orientation.North ? current.Rotated(1) : current.Rotated(-1);
|
||||
|
||||
// set state but only check solution on the last change
|
||||
SetState(stateIndex, current.AngleInt(), i == action.stateIndices.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly] // used for field validation
|
||||
private bool CorrectRotatorCount(List<Rotator> list) => list != null && list.Count == stateCount;
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Puzzle B/PuzzleB.cs.meta
Normal file
3
Assets/Station46/Runtime/Puzzle B/PuzzleB.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b80d4bcd2f6465189d94b993e13a034
|
||||
timeCreated: 1669688216
|
||||
36
Assets/Station46/Runtime/Puzzle B/Rotator.cs
Normal file
36
Assets/Station46/Runtime/Puzzle B/Rotator.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
|
||||
{
|
||||
/// <summary>
|
||||
/// The rotator component used by the hexagon module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Emission))]
|
||||
public class Rotator : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The angle this rotator should rotate towards.")]
|
||||
[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; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Emission = GetComponent<Emission>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
angle %= 360;
|
||||
var angleDifference = angle - transform.localEulerAngles.y;
|
||||
if (Math.Abs(angleDifference) >= 0.1)
|
||||
{
|
||||
transform.Rotate(0, angleDifference*Mathf.Pow(speed, 2), 0, Space.Self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Station46/Runtime/Puzzle B/Rotator.cs.meta
Normal file
11
Assets/Station46/Runtime/Puzzle B/Rotator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4596ccc5a8dcfb4294f3932f43e1847
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/Station46/Runtime/Puzzle C.meta
Normal file
3
Assets/Station46/Runtime/Puzzle C.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ec2f8925bad45d189d951bc02b39c11
|
||||
timeCreated: 1669815308
|
||||
62
Assets/Station46/Runtime/Puzzle C/Hole.cs
Normal file
62
Assets/Station46/Runtime/Puzzle C/Hole.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using EscapeRoomEngine.Desert.Runtime.Dispenser;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
||||
{
|
||||
/// <summary>
|
||||
/// A hole used in the <see cref="Holes"/> module.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Emission), typeof(Collider))]
|
||||
public class Hole : Button
|
||||
{
|
||||
[Tooltip("The number of this hole, starting from the bottom left.")]
|
||||
[Min(0)]
|
||||
public int number;
|
||||
|
||||
public Emission Emission { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Emission = GetComponent<Emission>();
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
Emission.active = true;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
var orb = other.GetComponent<DispenserOrb>();
|
||||
if (orb != null)
|
||||
{
|
||||
if (Active)
|
||||
{
|
||||
var color = Engine.Runtime.Engine.Theme.activeColor;
|
||||
|
||||
orb.Color = color.hdr;
|
||||
Emission.color = color.hdr;
|
||||
Press();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
var orb = other.GetComponent<DispenserOrb>();
|
||||
if (orb != null)
|
||||
{
|
||||
var color = Engine.Runtime.Engine.Theme.puzzleColor;
|
||||
|
||||
if (Active)
|
||||
{
|
||||
Release();
|
||||
Emission.color = color.hdr;
|
||||
}
|
||||
orb.Color = color.hdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Puzzle C/Hole.cs.meta
Normal file
3
Assets/Station46/Runtime/Puzzle C/Hole.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05ccb586cbc54f9e8c46ade911449f7f
|
||||
timeCreated: 1669815357
|
||||
93
Assets/Station46/Runtime/Puzzle C/Holes.cs
Normal file
93
Assets/Station46/Runtime/Puzzle C/Holes.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using EscapeRoomEngine.Engine.Runtime.Modules;
|
||||
using EscapeRoomEngine.Engine.Runtime.Utilities;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EscapeRoomEngine.Desert.Runtime.Puzzle_C
|
||||
{
|
||||
/// <summary>
|
||||
/// The main component for the orb grid module.
|
||||
/// </summary>
|
||||
public class Holes : StatePuzzle
|
||||
{
|
||||
[BoxGroup("Internal")] [SerializeField]
|
||||
private Emission frameLight;
|
||||
|
||||
private List<Hole> _holes;
|
||||
private Dispenser.Dispenser _dispenser;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_holes = new List<Hole>(GetComponentsInChildren<Hole>());
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
PuzzleEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (type)
|
||||
{
|
||||
case PuzzleEventType.Restarted:
|
||||
var color = Engine.Runtime.Engine.Theme.puzzleColor;
|
||||
_holes.ForEach(hole =>
|
||||
{
|
||||
hole.Enable();
|
||||
hole.Emission.color = color.hdr;
|
||||
});
|
||||
frameLight.color = color.hdr;
|
||||
_dispenser.Reset();
|
||||
break;
|
||||
case PuzzleEventType.Solved:
|
||||
var solvedColor = Engine.Runtime.Engine.Theme.solvedColor;
|
||||
var activeColor = Engine.Runtime.Engine.Theme.activeColor;
|
||||
_holes.ForEach(hole =>
|
||||
{
|
||||
hole.Disable();
|
||||
hole.Emission.color = solution[hole.number] == 1 ? activeColor.hdr : solvedColor.hdr;
|
||||
});
|
||||
frameLight.color = solvedColor.hdr;
|
||||
_dispenser.Solve();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
_holes.ForEach(hole => hole.ButtonEvent += (_, type) =>
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (type)
|
||||
{
|
||||
case ButtonEventType.Pressed:
|
||||
SetState(hole.number, 1, true);
|
||||
break;
|
||||
case ButtonEventType.Released:
|
||||
SetState(hole.number, 0, true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
frameLight.active = true;
|
||||
}
|
||||
|
||||
public override void SetModule(Module module)
|
||||
{
|
||||
base.SetModule(module);
|
||||
|
||||
// The holes require a related dispenser module
|
||||
var firstRelatedModule = Module.relatedModules[0];
|
||||
if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
|
||||
{
|
||||
_dispenser = dispenser;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EngineException("Holes were not assigned a related Dispenser.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Station46/Runtime/Puzzle C/Holes.cs.meta
Normal file
3
Assets/Station46/Runtime/Puzzle C/Holes.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce1763e3c7644933af2ff398ba6355b9
|
||||
timeCreated: 1669815316
|
||||
18
Assets/Station46/Runtime/Station46.asmdef
Normal file
18
Assets/Station46/Runtime/Station46.asmdef
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Station46",
|
||||
"rootNamespace": "EscapeRoomEngine",
|
||||
"references": [
|
||||
"GUID:2d68e204354e44f2a2ecf3cfa9213c5f",
|
||||
"GUID:ba4c7dba98ca4c31818cc46276b5dea1",
|
||||
"GUID:776d03a35f1b52c4a9aed9f56d7b4229"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
3
Assets/Station46/Runtime/Station46.asmdef.meta
Normal file
3
Assets/Station46/Runtime/Station46.asmdef.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20d0c4bd521546c7859e37019a165e38
|
||||
timeCreated: 1668940232
|
||||
Reference in New Issue
Block a user