265 lines
7.6 KiB
C#
265 lines
7.6 KiB
C#
using OctoCore;
|
|
using OctoCore.Components;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using Unity.VisualScripting;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
public class SpiralStaircase : MonoBehaviour
|
|
{
|
|
[System.Serializable]
|
|
public class DoorsPair
|
|
{
|
|
public Transform Left;
|
|
public Transform Right;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class State
|
|
{
|
|
public bool PlayerInside;
|
|
public int Turns;
|
|
public float TotalAngle;
|
|
[Range(0, 1)] public float Progression;
|
|
[Range(0, 1)] public float TotalProgression;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class References
|
|
{
|
|
public UniversalRendererData RendererData;
|
|
public GameObject Stairs;
|
|
public DoorsPair[] Doors;
|
|
public GameObject[] Inside;
|
|
public GameObject[] Outside;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class Settings
|
|
{
|
|
public float Offset = -12.25f;
|
|
public float StartAngle = 450;
|
|
public float EndAngle = 720;
|
|
public float AdditionalTopAngle = 360;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class HallucinationTrigger
|
|
{
|
|
[Range(0, 1)] public float Progression;
|
|
public UnityEvent OnReached;
|
|
[HideInInspector] public bool Triggered;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class Events
|
|
{
|
|
public UnityEvent<bool> OnInside;
|
|
public UnityEvent OnTopReached;
|
|
public UnityEvent OnBottomReached;
|
|
public HallucinationTrigger[] HallucinationTriggers;
|
|
}
|
|
|
|
[SerializeField, OctoReadOnly] private State _state;
|
|
[SerializeField] private References _references;
|
|
[SerializeField] private Settings _settings;
|
|
[SerializeField] private Events _events;
|
|
|
|
private List<IEnumerator> _doorsCoroutine;
|
|
|
|
private float _entryAngle = 0f;
|
|
|
|
void Awake()
|
|
{
|
|
_doorsCoroutine = new List<IEnumerator>();
|
|
for (int i = 0; i < _references.Doors.Length; i++)
|
|
_doorsCoroutine.Add(null);
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
ManageInside();
|
|
_events.OnBottomReached?.Invoke();
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
_state.PlayerInside = false;
|
|
ManageInside();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (_state.PlayerInside)
|
|
Count();
|
|
}
|
|
|
|
void OnTriggerEnter(Collider col)
|
|
{
|
|
_entryAngle = _state.TotalAngle;
|
|
if (OctoPlayer.Instance && OctoPlayer.Instance.gameObject == col.gameObject)
|
|
{
|
|
if (!_state.PlayerInside)
|
|
{
|
|
_state.PlayerInside = true;
|
|
ManageInside();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnTriggerExit(Collider col)
|
|
{
|
|
if (OctoPlayer.Instance && OctoPlayer.Instance.gameObject == col.gameObject)
|
|
{
|
|
if (_state.PlayerInside)
|
|
{
|
|
_state.PlayerInside = false;
|
|
ManageInside();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OpendDoors(bool open) { for (int i = 0; i < _references.Doors.Length; i++) OpendDoor(i, open); }
|
|
public void OpendDoor(int id) { OpendDoor(id, true); }
|
|
public void CloseDoor(int id) { OpendDoor(id, false); }
|
|
public void SetDoorOpened(int id) { OpendDoor(id, true, false); }
|
|
public void SetDoorClosed(int id) { OpendDoor(id, false, false); }
|
|
|
|
private void OpendDoor(int id, bool open, bool animate = true)
|
|
{
|
|
if (_doorsCoroutine[id] != null) StopCoroutine(_doorsCoroutine[id]);
|
|
StartCoroutine(_doorsCoroutine[id] = OpenDoorCoroutine(id, open, animate));
|
|
}
|
|
|
|
IEnumerator OpenDoorCoroutine(int id, bool open, bool animate)
|
|
{
|
|
const float duration = .6f;
|
|
|
|
DoorsPair door = _references.Doors[id];
|
|
|
|
Quaternion originRotL = door.Left.transform.localRotation;
|
|
Quaternion originRotR = door.Right.transform.localRotation;
|
|
Quaternion targetRotL = Quaternion.Euler(0, open ? 0 : 13.1f, 0);
|
|
Quaternion targetRotR = Quaternion.Euler(0, open ? 0 :-13.1f, 0);
|
|
|
|
if (animate)
|
|
{
|
|
for (float t = 0; t < duration; t += Time.deltaTime)
|
|
{
|
|
float lerp = .5f - Mathf.Cos(t / duration * Mathf.PI) * .5f;
|
|
|
|
door.Left.transform.localRotation = Quaternion.Lerp(originRotL, targetRotL, lerp);
|
|
door.Right.transform.localRotation = Quaternion.Lerp(originRotR, targetRotR, lerp);
|
|
|
|
yield return new WaitForEndOfFrame();
|
|
}
|
|
}
|
|
|
|
door.Left.transform.localRotation = targetRotL;
|
|
door.Right.transform.localRotation = targetRotR;
|
|
|
|
_doorsCoroutine[id] = null;
|
|
}
|
|
|
|
public void SimulateTop()
|
|
{
|
|
_state.Progression = 0;
|
|
_state.PlayerInside = true;
|
|
_state.Turns = (int)_settings.EndAngle / 90;
|
|
Count();
|
|
ManageInside();
|
|
}
|
|
|
|
public void SimulateBottom()
|
|
{
|
|
_state.Progression = 1;
|
|
_state.PlayerInside = true;
|
|
_state.Turns = 0;
|
|
Count();
|
|
ManageInside();
|
|
}
|
|
|
|
private void Count()
|
|
{
|
|
if (!OctoPlayer.Instance) return;
|
|
|
|
Vector2[] steps = new Vector2[4] { Vector2.up, Vector2.left, Vector2.down, Vector2.right };
|
|
Vector2 nextStep = steps[_state.Turns % steps.Length];
|
|
|
|
Vector3 relativePos3D = transform.InverseTransformPoint(OctoPlayer.Instance.transform.position);
|
|
Vector2 relativePos2D = new Vector2(relativePos3D.x, relativePos3D.z);
|
|
|
|
float quarterAngle = Vector2.Angle(nextStep, relativePos2D) * Mathf.Sign(Vector3.Cross(nextStep, relativePos2D).z);
|
|
|
|
_state.TotalAngle = Mathf.Max(0, _state.Turns * 90 + quarterAngle);
|
|
|
|
if (quarterAngle > 90 && quarterAngle < 100)
|
|
{
|
|
_state.Turns++;
|
|
}
|
|
else if (quarterAngle < 0 && _state.Turns > 0)
|
|
{
|
|
_state.Turns--;
|
|
}
|
|
|
|
float progression = Mathf.Clamp01(Mathf.InverseLerp(_settings.StartAngle, _settings.EndAngle, _state.TotalAngle));
|
|
|
|
if (progression >= .5f && _state.Progression < .5f)
|
|
{
|
|
_events.OnTopReached?.Invoke();
|
|
}
|
|
else if (progression < .5f && _state.Progression >= .5f)
|
|
{
|
|
_events.OnBottomReached?.Invoke();
|
|
}
|
|
|
|
_state.Progression = progression;
|
|
|
|
_state.TotalProgression = Mathf.Clamp01(
|
|
Mathf.InverseLerp(_entryAngle, _settings.EndAngle + _settings.AdditionalTopAngle, _state.TotalAngle)
|
|
);
|
|
|
|
foreach (var trigger in _events.HallucinationTriggers)
|
|
{
|
|
if (!trigger.Triggered && _state.TotalProgression >= trigger.Progression)
|
|
{
|
|
trigger.Triggered = true;
|
|
trigger.OnReached?.Invoke();
|
|
}
|
|
else if (trigger.Triggered && _state.TotalProgression < trigger.Progression)
|
|
{
|
|
trigger.Triggered = false;
|
|
}
|
|
}
|
|
|
|
_references.Stairs.transform.localPosition = new Vector3(0, _state.Progression * _settings.Offset, 0);
|
|
}
|
|
|
|
private void ManageInside()
|
|
{
|
|
foreach (GameObject obj in _references.Inside) obj.SetActive(_state.PlayerInside);
|
|
foreach (GameObject obj in _references.Outside) obj.SetActive(!_state.PlayerInside);
|
|
|
|
_events.OnInside?.Invoke(_state.PlayerInside);
|
|
|
|
/*Shader.SetGlobalFloat("_MaskEnabled", _state.PlayerInside ? 0 : 1);
|
|
|
|
foreach (var feature in _references.RendererData.rendererFeatures)
|
|
{
|
|
// Stencil Mask
|
|
// VisibleThroughMask
|
|
// HiddenThroughMask
|
|
|
|
if (feature.name == "HiddenThroughMask")
|
|
{
|
|
feature.SetActive(!_state.PlayerInside);
|
|
break;
|
|
}
|
|
}*/
|
|
}
|
|
}
|