Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
Система выдачи призов хоппером: - 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
182 lines
7.3 KiB
C#
182 lines
7.3 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Threading;
|
||
using RR.Common.Managers;
|
||
using RR.Game2D.Core.Managers;
|
||
using UnityEngine;
|
||
|
||
namespace RR.Common.Controls
|
||
{
|
||
/// <summary>
|
||
/// Система управления выдачей призов и ведения статистики игры.
|
||
/// Отвечает за процесс выдачи призов, подсчет запусков и наград.
|
||
/// </summary>
|
||
public class PrizeSystem : MonoBehaviour
|
||
{
|
||
private static PrizeSystem Instance { get; set; }
|
||
|
||
private static Coroutine prizeCoroutine;
|
||
private static bool _prizing;
|
||
private static bool _lastPrizeSuccess = true;
|
||
|
||
/// <summary>
|
||
/// Потокобезопасный буфер для сигнала от датчика хоппера.
|
||
/// Записывается из любого потока (ComDeviceClient, COMInput), читается из корутины Prize().
|
||
/// </summary>
|
||
private static int _prizeGivenBuffer;
|
||
|
||
/// <summary>
|
||
/// Устанавливает флаг выдачи приза. Потокобезопасно — можно вызывать из любого потока.
|
||
/// </summary>
|
||
public static void SignalPrizeGiven()
|
||
{
|
||
Interlocked.Exchange(ref _prizeGivenBuffer, 1);
|
||
}
|
||
|
||
private static void ResetPrizeBuffer()
|
||
{
|
||
Interlocked.Exchange(ref _prizeGivenBuffer, 0);
|
||
}
|
||
|
||
private static bool IsPrizeBufferSet()
|
||
{
|
||
return Interlocked.CompareExchange(ref _prizeGivenBuffer, 0, 0) == 1;
|
||
}
|
||
|
||
public static readonly string key_starts = "STARTS";
|
||
public static readonly string key_startsNul = "STARTSNUL";
|
||
public static readonly string key_prizesgiven = "PRIZESGIVEN";
|
||
public static readonly string key_prizesgivenNul = "PRIZESGIVENNUL";
|
||
|
||
[SerializeField] AudioClip prizeErrorSound;
|
||
|
||
/// <summary>
|
||
/// Вызывается после завершения выдачи приза. true — успех, false — ошибка.
|
||
/// </summary>
|
||
public static event Action<bool> OnPrizeResult;
|
||
|
||
//===========================start: PUBLIC API===========================//
|
||
/// <summary>
|
||
/// Возвращает true, если последняя выдача приза прошла успешно, false - если была ошибка.
|
||
/// </summary>
|
||
public static bool WasLastPrizeSuccessful => _lastPrizeSuccess;
|
||
|
||
/// <summary>
|
||
/// Возвращает true, если в данный момент идет процесс выдачи приза.
|
||
/// </summary>
|
||
public static bool IsPrizing => _prizing;
|
||
|
||
/// <summary>
|
||
/// Инициализирует систему призов и счетчики статистики.
|
||
/// Должен быть вызван один раз при запуске приложения.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// Выполняет:
|
||
/// - Установку статического экземпляра (singleton)
|
||
/// - Инициализацию счетчиков в PlayerPrefs если они отсутствуют
|
||
/// - Подготовку системы к работе
|
||
/// </remarks>
|
||
public void Initialize()
|
||
{
|
||
if (Instance != null && Instance != this)
|
||
{
|
||
Destroy(gameObject);
|
||
return;
|
||
}
|
||
|
||
Instance = this;
|
||
DontDestroyOnLoad(gameObject);
|
||
|
||
InitCounters();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Запускает процесс выдачи физического приза игроку.
|
||
/// Проверяет настройки и текущее состояние перед началом выдачи.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// Условия для выдачи приза:
|
||
/// - В настройках включена выдача призов при игре с ботом
|
||
/// - В данный момент не выполняется другая выдача приза
|
||
///
|
||
/// Процесс выдачи:
|
||
/// - Включает мотор хопера
|
||
/// - Ожидает подтверждения выдачи от датчиков (10 сек таймаут)
|
||
/// - Выключает мотор по завершению
|
||
/// - Обновляет счетчики выданных призов
|
||
/// - Воспроизводит звук ошибки при неудаче
|
||
/// </remarks>
|
||
public static void GivePrize()
|
||
{
|
||
// bool settingEnabled = SettingsManager.instance.GetSettingValue("Выдача призов при игре с ботом") == "Выдавать";
|
||
//
|
||
// if (!settingEnabled) { DebugWindow.Message("Выдача приза отключена в настройках"); return; }
|
||
if (_prizing)
|
||
{
|
||
GameManager.Logger.Log("PrizeSystem", "Уже идет выдача приза. Не спамь кнопку.");
|
||
return;
|
||
}
|
||
//
|
||
// DebugWindow.Message("Выдаю приз");
|
||
if (prizeCoroutine != null) Instance.StopCoroutine(prizeCoroutine);
|
||
prizeCoroutine = Instance.StartCoroutine(Instance.Prize());
|
||
}
|
||
|
||
|
||
//===========================end: PUBLIC API===========================//
|
||
|
||
#region Приватные методы
|
||
private IEnumerator Prize()
|
||
{
|
||
_prizing = true;
|
||
// DebugWindow.Message("Начинаю выдачу");
|
||
|
||
|
||
const float timeout = 10; //сек
|
||
ComSystemManager.Send("301");//включаем мотор
|
||
float startTime = Time.time;
|
||
bool success = false;
|
||
|
||
ResetPrizeBuffer();
|
||
|
||
while (Time.time - startTime < timeout)
|
||
{
|
||
if (IsPrizeBufferSet())
|
||
{
|
||
success = true;
|
||
IncreasePrizeCounters();
|
||
break;
|
||
}
|
||
yield return null;
|
||
}
|
||
|
||
//Выключаем мотор
|
||
ComSystemManager.Send("300");
|
||
// DebugWindow.Message("Закончил. Успех: " + success);
|
||
|
||
_lastPrizeSuccess = success;
|
||
_prizing = false;
|
||
|
||
if (!success) AudioManager.Instance.PlaySfx(prizeErrorSound);
|
||
|
||
OnPrizeResult?.Invoke(success);
|
||
}
|
||
|
||
|
||
private static void IncreasePrizeCounters()
|
||
{
|
||
PlayerPrefs.SetInt(key_prizesgiven, PlayerPrefs.GetInt(key_prizesgiven) + 1);
|
||
PlayerPrefs.SetInt(key_prizesgivenNul, PlayerPrefs.GetInt(key_prizesgivenNul) + 1);
|
||
}
|
||
|
||
private static void InitCounters()
|
||
{
|
||
if (!PlayerPrefs.HasKey(key_starts)) PlayerPrefs.SetInt(key_starts, 0);
|
||
if (!PlayerPrefs.HasKey(key_startsNul)) PlayerPrefs.SetInt(key_startsNul, 0);
|
||
if (!PlayerPrefs.HasKey(key_prizesgiven)) PlayerPrefs.SetInt(key_prizesgiven, 0);
|
||
if (!PlayerPrefs.HasKey(key_prizesgivenNul)) PlayerPrefs.SetInt(key_prizesgivenNul, 0);
|
||
}
|
||
#endregion
|
||
}
|
||
}
|