Danro/Assets/Scripts/UI/PrizeErrorDisplay.cs
Danil Iarosh 6da2812c8e
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat: add hopper prize system, prize error display, levitator component
Система выдачи призов хоппером:
- PrizeSystemManager теперь читает enableHopper из настройки prizes.giveSmallPrize
  вместо захардкоженного SerializeField (реагирует на изменения в реальном времени)
- LevelFinish: исправлен ключ настройки на prizes.giveSmallPrize
- Добавлен HopperCheckController для тестирования выдачи приза кнопкой в настройках
- Проверка датчика хоппера перенесена в ComDeviceClient (2-й бит INPUT)
- PrizeGivenBuffer заменён с PlayerPrefs на потокобезопасный Interlocked-буфер
  (исправлен краш при вызове PlayerPrefs из фонового потока платы)
- Добавлен PrizeSystem.OnPrizeResult (Action<bool>) — событие результата выдачи

Отображение ошибки выдачи приза:
- Добавлен PrizeErrorDisplay — UI-компонент, подписанный на OnPrizeResult
- Зависит от настройки prizes.showPrizeError (реагирует на изменения в реальном времени)

QR-коды:
- PrizeSystemManager подписан на изменение prizes.phoneNumber — QR-коды
  перегенерируются автоматически при смене номера
- WinBannerController показывает QR-коды только при включённой prizes.giveBigPrize

Прочее:
- Добавлен Levitator — компонент плавной левитации объекта (высота, скорость, плавность)
- Настройки: группы поддержки/тестов реорганизованы, добавлена кнопка btn.hopper_test
2026-03-06 16:47:40 +03:00

154 lines
5.0 KiB
C#
Raw 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 UnityEngine;
using TMPro;
using RR.Common.Controls;
using RR.Common.Settings;
namespace RR.Common.UI
{
/// <summary>
/// UI-компонент для отображения ошибки выдачи приза хоппером.
/// Подписывается на PrizeSystem.OnPrizeResult и показывает сообщение об ошибке,
/// если выдача приза завершилась неудачно (таймаут датчика).
///
/// Зависит от настройки prizes.showPrizeError.
/// </summary>
public class PrizeErrorDisplay : MonoBehaviour
{
[Header("UI")]
[Tooltip("Текст сообщения об ошибке. Если не задан, ищется на этом же объекте.")]
[SerializeField] private TextMeshProUGUI errorText;
[Tooltip("Объект-контейнер ошибки (для скрытия/показа). Если не задан, используется gameObject.")]
[SerializeField] private GameObject errorContainer;
[Header("Настройки")]
[Tooltip("Текст ошибки, который отображается игроку.")]
[SerializeField] private string errorMessage = "Ошибка выдачи приза!";
[Header("Анимация")]
[Tooltip("CanvasGroup для плавного появления/исчезновения.")]
[SerializeField] private CanvasGroup canvasGroup;
[Tooltip("Длительность анимации появления/исчезновения.")]
[SerializeField] private float fadeDuration = 0.3f;
private bool _enabled;
private bool _isShowing;
private Coroutine _fadeCoroutine;
private void OnEnable()
{
if (errorContainer == null) errorContainer = gameObject;
if (errorText == null) errorText = GetComponentInChildren<TextMeshProUGUI>();
Hide(instant: true);
_enabled = SettingsManager.Instance != null
&& SettingsManager.Instance.GetBool("prizes.showPrizeError");
if (SettingsManager.Instance != null)
SettingsManager.Instance.OnSettingChanged += OnSettingChanged;
PrizeSystem.OnPrizeResult += OnPrizeResult;
}
private void OnDisable()
{
if (SettingsManager.Instance != null)
SettingsManager.Instance.OnSettingChanged -= OnSettingChanged;
PrizeSystem.OnPrizeResult -= OnPrizeResult;
}
private void OnSettingChanged(string key, SettingValue value)
{
if (key != "prizes.showPrizeError") return;
_enabled = value.b;
if (!_enabled)
Hide(instant: true);
}
private void OnPrizeResult(bool success)
{
if (success)
{
if (_isShowing) Hide();
}
else
{
Show();
}
}
/// <summary>
/// Показывает сообщение об ошибке выдачи приза.
/// </summary>
public void Show()
{
if (!_enabled) return;
_isShowing = true;
if (errorText) errorText.text = errorMessage;
errorContainer.SetActive(true);
if (canvasGroup)
{
if (_fadeCoroutine != null) StopCoroutine(_fadeCoroutine);
_fadeCoroutine = StartCoroutine(Fade(0f, 1f, fadeDuration));
}
Debug.LogWarning("[PrizeErrorDisplay] Ошибка выдачи приза отображена.");
}
/// <summary>
/// Скрывает сообщение об ошибке.
/// </summary>
public void Hide(bool instant = false)
{
_isShowing = false;
if (_fadeCoroutine != null)
{
StopCoroutine(_fadeCoroutine);
_fadeCoroutine = null;
}
if (instant || !canvasGroup)
{
if (canvasGroup) canvasGroup.alpha = 0f;
errorContainer.SetActive(false);
}
else
{
_fadeCoroutine = StartCoroutine(FadeAndDisable());
}
}
private System.Collections.IEnumerator FadeAndDisable()
{
yield return Fade(canvasGroup.alpha, 0f, fadeDuration);
errorContainer.SetActive(false);
_fadeCoroutine = null;
}
private System.Collections.IEnumerator Fade(float from, float to, float duration)
{
if (!canvasGroup) yield break;
float elapsed = 0f;
canvasGroup.alpha = from;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
canvasGroup.alpha = Mathf.Lerp(from, to, elapsed / duration);
yield return null;
}
canvasGroup.alpha = to;
}
}
}