chip-gameV3/Assets/Scripts/Settings/SettingsPasswordPanel.cs

371 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Text;
using DG.Tweening;
using TMPro;
using UnityEngine;
public sealed class SettingsPasswordPanel : MonoBehaviour, IControlable
{
public event Action OnAccepted;
public event Action OnCancelled;
[SerializeField] private CanvasGroup _canvasGroup;
[SerializeField] private TextMeshProUGUI _title;
[SerializeField] private TextMeshProUGUI _combination;
[SerializeField] private TextMeshProUGUI _hint;
[SerializeField] private TextMeshProUGUI _status;
[SerializeField] private RectTransform _confirmProgressFill;
[SerializeField] private CanvasGroup _confirmationGroup;
[SerializeField] private TextMeshProUGUI[] _confirmationOptions;
[SerializeField] private float _confirmHoldDuration = 1f;
[SerializeField] private float _cancelHoldDuration = 1f;
[SerializeField] private float _fadeDuration = 0.25f;
[SerializeField] private Color _normalOptionColor = Color.white;
[SerializeField] private Color _selectedOptionColor = new Color(1f, 0.85f, 0.1f, 1f);
[SerializeField] private Color _errorColor = new Color(1f, 0.25f, 0.25f, 1f);
private const int PasswordLength = 6;
private const int SaveOptionIndex = 0;
private const int ReenterOptionIndex = 1;
private const int CancelOptionIndex = 2;
private readonly StringBuilder _enteredCombination = new StringBuilder(PasswordLength);
private Tween _fadeTween;
private PanelPurpose _purpose;
private PanelStage _stage;
private float _confirmHoldTime;
private float _cancelHoldTime;
private float _statusHideTime;
private int _selectedOptionIndex;
private bool _actionReleased;
private bool _serviceReleased;
private bool _isClosing;
public bool ControlsEnabled { get; set; }
private void OnDisable()
{
_fadeTween?.Kill();
_fadeTween = null;
ControlsEnabled = false;
}
public void OpenForSettingsAccess()
{
_purpose = PanelPurpose.SettingsAccess;
Open(PanelStage.VerifyPassword);
}
public void OpenForPasswordChange()
{
_purpose = PanelPurpose.PasswordChange;
Open(SettingsPasswordStore.HasPassword ? PanelStage.VerifyPassword : PanelStage.EnterNewPassword);
}
public void UpdateControls()
{
UpdateStatus();
UpdateCancelInput();
if (_isClosing)
return;
if (_stage == PanelStage.ConfirmPassword)
UpdateConfirmationControls();
else
UpdatePasswordControls();
}
private void Open(PanelStage initialStage)
{
_fadeTween?.Kill();
transform.SetAsLastSibling();
_canvasGroup.alpha = 0f;
_canvasGroup.blocksRaycasts = true;
_isClosing = false;
_serviceReleased = false;
ControlsEnabled = true;
SetStage(initialStage);
_fadeTween = _canvasGroup.DOFade(1f, _fadeDuration).SetEase(Ease.OutCubic).SetUpdate(true);
_fadeTween.PlayForward();
}
private void UpdatePasswordControls()
{
Vector2Int axis = In.GetAxis(JoystickSide.Both, InteractMode.Discrete);
if (_enteredCombination.Length < PasswordLength)
AddDirection(axis);
bool actionHeld = In.GetAction(JoystickSide.Both, InteractMode.Continious);
if (actionHeld == false)
_actionReleased = true;
bool canConfirm = _enteredCombination.Length == PasswordLength && _actionReleased;
_confirmHoldTime = canConfirm && actionHeld
? Mathf.Min(_confirmHoldTime + Time.unscaledDeltaTime, _confirmHoldDuration)
: 0f;
SetConfirmProgress(_confirmHoldTime / _confirmHoldDuration);
if (_confirmHoldTime < _confirmHoldDuration)
return;
_confirmHoldTime = 0f;
_actionReleased = false;
SetConfirmProgress(0f);
if (_stage == PanelStage.VerifyPassword)
VerifyPassword();
else
SetStage(PanelStage.ConfirmPassword);
}
private void UpdateConfirmationControls()
{
Vector2Int axis = In.GetAxis(JoystickSide.Both, InteractMode.Discrete);
int selectionDirection = axis.y != 0 ? -axis.y : axis.x;
if (selectionDirection != 0)
{
_selectedOptionIndex = (_selectedOptionIndex + Math.Sign(selectionDirection) + _confirmationOptions.Length)
% _confirmationOptions.Length;
UpdateConfirmationSelection();
}
bool actionHeld = In.GetAction(JoystickSide.Both, InteractMode.Continious);
if (actionHeld == false)
_actionReleased = true;
if (_actionReleased && In.GetAction(JoystickSide.Both, InteractMode.Discrete))
ExecuteConfirmationOption();
}
private void UpdateCancelInput()
{
bool serviceHeld = In.GetService();
if (serviceHeld == false)
_serviceReleased = true;
_cancelHoldTime = _serviceReleased && serviceHeld
? Mathf.Min(_cancelHoldTime + Time.unscaledDeltaTime, _cancelHoldDuration)
: 0f;
if (_cancelHoldTime >= _cancelHoldDuration)
Close(false);
}
private void AddDirection(Vector2Int axis)
{
char direction = GetDirectionSymbol(axis);
if (direction == default)
return;
_enteredCombination.Append(direction);
UpdateCombinationView();
ClearStatus();
}
private void VerifyPassword()
{
if (SettingsPasswordStore.Validate(_enteredCombination.ToString()))
{
if (_purpose == PanelPurpose.SettingsAccess)
Close(true);
else
SetStage(PanelStage.EnterNewPassword);
return;
}
_enteredCombination.Clear();
UpdateCombinationView();
ShowStatus("НЕВЕРНЫЙ ПАРОЛЬ", _errorColor, 1.5f);
}
private void ExecuteConfirmationOption()
{
_actionReleased = false;
if (_selectedOptionIndex == SaveOptionIndex)
{
if (SettingsPasswordStore.Save(_enteredCombination.ToString()))
Close(true);
else
ShowStatus("НЕ УДАЛОСЬ СОХРАНИТЬ ПАРОЛЬ", _errorColor, 2f);
return;
}
if (_selectedOptionIndex == ReenterOptionIndex)
{
SetStage(PanelStage.EnterNewPassword);
return;
}
if (_selectedOptionIndex == CancelOptionIndex)
Close(false);
}
private void SetStage(PanelStage stage)
{
_stage = stage;
if (stage != PanelStage.ConfirmPassword)
_enteredCombination.Clear();
_confirmHoldTime = 0f;
_selectedOptionIndex = 0;
_actionReleased = false;
SetConfirmProgress(0f);
ClearStatus();
bool confirmationVisible = stage == PanelStage.ConfirmPassword;
_confirmationGroup.alpha = confirmationVisible ? 1f : 0f;
_confirmationGroup.blocksRaycasts = confirmationVisible;
_combination.gameObject.SetActive(confirmationVisible == false);
_confirmProgressFill.parent.gameObject.SetActive(confirmationVisible == false);
if (stage == PanelStage.VerifyPassword)
{
_title.text = _purpose == PanelPurpose.SettingsAccess ? "ВВЕДИТЕ ПАРОЛЬ" : "СТАРЫЙ ПАРОЛЬ";
_hint.text = "Введите 6 движений любым джойстиком\nи удерживайте кнопку действия 1 секунду";
}
else if (stage == PanelStage.EnterNewPassword)
{
_title.text = "НОВЫЙ ПАРОЛЬ";
_hint.text = "Введите 6 движений любым джойстиком\nи удерживайте кнопку действия 1 секунду";
}
else
{
_title.text = "ПОДТВЕРЖДЕНИЕ";
_hint.text = $"Сохранить комбинацию {FormatCombination(_enteredCombination.ToString())}?";
UpdateConfirmationSelection();
}
UpdateCombinationView();
}
private void UpdateCombinationView()
=> _combination.text = FormatCombination(_enteredCombination.ToString());
private void UpdateConfirmationSelection()
{
for (int i = 0; i < _confirmationOptions.Length; i++)
{
bool selected = i == _selectedOptionIndex;
_confirmationOptions[i].color = selected ? _selectedOptionColor : _normalOptionColor;
_confirmationOptions[i].rectTransform.localScale = selected ? Vector3.one * 1.08f : Vector3.one;
}
}
private void SetConfirmProgress(float progress)
{
Vector2 anchorMax = _confirmProgressFill.anchorMax;
anchorMax.x = Mathf.Clamp01(progress);
_confirmProgressFill.anchorMax = anchorMax;
}
private void ShowStatus(string message, Color color, float duration)
{
_status.text = message;
_status.color = color;
_statusHideTime = Time.unscaledTime + duration;
}
private void UpdateStatus()
{
if (_statusHideTime > 0f && Time.unscaledTime >= _statusHideTime)
ClearStatus();
}
private void ClearStatus()
{
_status.text = string.Empty;
_statusHideTime = 0f;
}
private void Close(bool accepted)
{
if (_isClosing)
return;
_isClosing = true;
ControlsEnabled = false;
_fadeTween?.Kill();
_fadeTween = _canvasGroup.DOFade(0f, _fadeDuration).SetEase(Ease.InCubic).SetUpdate(true);
_fadeTween.OnComplete(() =>
{
_canvasGroup.blocksRaycasts = false;
if (accepted)
OnAccepted?.Invoke();
else
OnCancelled?.Invoke();
});
_fadeTween.PlayForward();
}
private static char GetDirectionSymbol(Vector2Int axis)
{
if (axis.y > 0)
return 'U';
if (axis.y < 0)
return 'D';
if (axis.x < 0)
return 'L';
if (axis.x > 0)
return 'R';
return default;
}
private static string FormatCombination(string combination)
{
var result = new StringBuilder();
for (int i = 0; i < PasswordLength; i++)
{
if (i > 0)
result.Append(" ");
result.Append(i < combination.Length ? GetDirectionArrow(combination[i]) : "·");
}
return result.ToString();
}
private static string GetDirectionArrow(char direction)
{
return direction switch
{
'U' => "↑",
'D' => "↓",
'L' => "←",
'R' => "→",
_ => "·"
};
}
private enum PanelPurpose
{
SettingsAccess,
PasswordChange
}
private enum PanelStage
{
VerifyPassword,
EnterNewPassword,
ConfirmPassword
}
}