Files
A-Fairytale-Gone-Bad-Briar-…/Assets/Scripts/Game/Puzzles/ChapterOne/LaxleyHouse/Clock/LaxleyClockHand.cs

316 lines
8.2 KiB
C#

using System.Threading;
using BriarQueen.Framework.Managers.Levels.Data;
using BriarQueen.Framework.Managers.Player.Data;
using Cysharp.Threading.Tasks;
using PrimeTween;
using UnityEngine;
namespace BriarQueen.Game.Puzzles.ChapterOne.LaxleyHouse.Clock
{
public class LaxleyClockHand : BaseItem
{
[Header("State")]
[SerializeField]
private bool _placed;
[SerializeField]
[Range(0, 11)]
private int _currentRotationStep;
[SerializeField]
private bool _locked;
[SerializeField]
private bool _isRotating;
[SerializeField]
private bool _canRotate;
[Header("Rotation")]
[SerializeField]
private float[] _rotationSteps = new float[12]
{
0f, // 12
-30f, // 1
-60f, // 2
-90f, // 3
-120f, // 4
-150f, // 5
-180f, // 6
-210f, // 7
-240f, // 8
-270f, // 9
-300f, // 10
-330f // 11
};
[SerializeField]
private float _rotateDuration = 0.15f;
[SerializeField]
private Ease _rotateEase = Ease.Linear;
[Header("Placement")]
[SerializeField]
private float _placeDuration = 0.2f;
[SerializeField]
private Ease _placeEase = Ease.OutSine;
[Header("Components")]
[SerializeField]
private GameObject _parentPivot;
[SerializeField]
private CanvasGroup _parentCanvasGroup;
[Header("Placement Behaviour")]
[SerializeField]
private bool _randomiseRotationOnFirstPlace = true;
private Sequence _placeSequence;
private Sequence _rotateSequence;
private CancellationTokenSource _placeCTS;
private CancellationTokenSource _rotateCTS;
private LaxleyClockFace _clockFace;
public bool Placed => _placed;
public int CurrentRotationStep => _currentRotationStep;
public void Initialise(LaxleyClockFace clockFace, LaxleyClockBasePuzzle owningPuzzle)
{
_clockFace = clockFace;
_canRotate = false;
if (_placed)
{
if (_parentPivot != null)
_parentPivot.SetActive(true);
if (_parentCanvasGroup != null)
_parentCanvasGroup.alpha = 1f;
ApplyRotationImmediate(_currentRotationStep);
}
else
{
if (_parentPivot != null)
_parentPivot.SetActive(false);
if (_parentCanvasGroup != null)
_parentCanvasGroup.alpha = 0f;
}
RefreshVisualState();
}
public override async UniTask OnInteract(ItemDataSo item = null)
{
if (!IsInteractable())
return;
await RotateToNextStep();
await _clockFace.NotifyHandChanged();
}
public async UniTask Place()
{
if (_placed)
return;
_placed = true;
_locked = false;
_canRotate = false;
CancelPlaceTween();
_placeCTS = new CancellationTokenSource();
if (_randomiseRotationOnFirstPlace)
_currentRotationStep = Random.Range(0, _rotationSteps.Length);
if (_parentPivot != null)
_parentPivot.SetActive(true);
ApplyRotationImmediate(_currentRotationStep);
if (_parentCanvasGroup != null)
{
_parentCanvasGroup.alpha = 0f;
_parentCanvasGroup.blocksRaycasts = false;
_parentCanvasGroup.interactable = false;
}
_placeSequence.Stop();
_placeSequence = Sequence.Create()
.Group(Tween.Alpha(_parentCanvasGroup, 1f, _placeDuration, _placeEase));
try
{
await _placeSequence.ToUniTask(cancellationToken: _placeCTS.Token);
}
catch
{
}
RefreshVisualState();
}
public void RestoreState(bool placed, int rotationStep, bool locked)
{
CancelPlaceTween();
CancelRotateTween();
_placed = placed;
_locked = locked;
_isRotating = false;
_canRotate = false;
_currentRotationStep = Mathf.Clamp(rotationStep, 0, 11);
if (_parentPivot != null)
_parentPivot.SetActive(_placed);
if (_placed)
{
ApplyRotationImmediate(_currentRotationStep);
if (_parentCanvasGroup != null)
_parentCanvasGroup.alpha = 1f;
}
else
{
if (_parentCanvasGroup != null)
_parentCanvasGroup.alpha = 0f;
}
RefreshVisualState();
}
public void SetCanRotate(bool canRotate)
{
_canRotate = canRotate;
RefreshVisualState();
}
public void Lock()
{
_locked = true;
_canRotate = false;
RefreshVisualState();
}
public void Hide()
{
CancelPlaceTween();
CancelRotateTween();
_canRotate = false;
_isRotating = false;
if (_parentCanvasGroup != null)
{
_parentCanvasGroup.blocksRaycasts = false;
_parentCanvasGroup.interactable = false;
_parentCanvasGroup.alpha = 0f;
}
if (_parentPivot != null)
_parentPivot.SetActive(false);
}
private async UniTask RotateToNextStep()
{
if (!IsInteractable())
return;
_isRotating = true;
RefreshVisualState();
CancelRotateTween();
_rotateCTS = new CancellationTokenSource();
_currentRotationStep = (_currentRotationStep + 1) % 12;
Vector3 targetEuler = _parentPivot.transform.localEulerAngles;
targetEuler.z = _rotationSteps[_currentRotationStep];
_rotateSequence.Stop();
_rotateSequence = Sequence.Create()
.Group(
Tween.LocalRotation(
_parentPivot.transform,
Quaternion.Euler(targetEuler),
_rotateDuration,
_rotateEase));
try
{
await _rotateSequence.ToUniTask(cancellationToken: _rotateCTS.Token);
}
catch
{
}
_isRotating = false;
RefreshVisualState();
}
private void ApplyRotationImmediate(int step)
{
if (_parentPivot == null)
return;
Vector3 euler = _parentPivot.transform.localEulerAngles;
euler.z = _rotationSteps[Mathf.Clamp(step, 0, 11)];
_parentPivot.transform.localEulerAngles = euler;
}
private void RefreshVisualState()
{
bool interactable = IsInteractable();
if (_parentCanvasGroup != null)
{
_parentCanvasGroup.blocksRaycasts = interactable;
_parentCanvasGroup.interactable = interactable;
}
}
private bool IsInteractable()
{
return _placed && !_locked && !_isRotating && _canRotate;
}
private void CancelPlaceTween()
{
_placeSequence.Stop();
if (_placeCTS != null)
{
_placeCTS.Cancel();
_placeCTS.Dispose();
_placeCTS = null;
}
}
private void CancelRotateTween()
{
_rotateSequence.Stop();
if (_rotateCTS != null)
{
_rotateCTS.Cancel();
_rotateCTS.Dispose();
_rotateCTS = null;
}
}
private void OnDestroy()
{
CancelPlaceTween();
CancelRotateTween();
}
}
}