using System; using System.Collections.Generic; using UnityEngine; using RR.Common.Prizes; using RR.Common.Settings; using RR.Common.UI; using RR.Common.Utility; using RR.Game2D.Core.Managers; namespace RR.Common.Managers { /// /// Центральный менеджер системы призов. /// Не знает конкретики о призах — читает конфигурацию из JSON () /// и делегирует выдачу зарегистрированным исполнителям (). /// public class PrizeSystemManager : MonoBehaviour { public static PrizeSystemManager Instance { get; private set; } [Header("QR-коды (глобальная конфигурация)")] [SerializeField] private List qrCodes = new(); [SerializeField] private string phoneNumberMask = "+7XXXXXXXXXX"; /// Хранилище призов за уровни (JSON). public PrizeDatabase Database { get; private set; } /// /// Вызывается после выдачи любого приза. Параметры: тип приза, успех. /// public event Action OnAnyPrizeResult; // ── Исполнители ── private readonly Dictionary _executors = new(); // ── QR-коды ── private readonly List _generatedQRCodes = new(); private Texture2D[] _qrCodesCache; private bool _qrCodesDirty = true; private string _phoneNumber = string.Empty; // ────────────── Инициализация ────────────── public void Initialize() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); // Загружаем базу призов из JSON Database = new PrizeDatabase(); Database.Load(); // QR-коды (глобальные) RefreshPhoneNumber(); GenerateQRCodes(); SubscribeToSettings(); // Регистрируем исполнителей — модальные окна регистрируют себя сами через static Current RegisterExecutor(new NonePrize()); RegisterExecutor(new QRCodePrize(() => QRCodeModal.Current, GetQRCodes)); RegisterExecutor(new HopperPrize()); RegisterExecutor(new PrizeStandPrize(() => PrizeStandModal.Current)); Debug.Log("[PrizeSystemManager] Инициализирован. " + $"Исполнители: {_executors.Count}, Уровней в базе: {Database.GetAll().Count}"); } private void OnDestroy() { UnsubscribeFromSettings(); if (Instance == this) Instance = null; } // ────────────── Публичный API ────────────── /// /// Выдаёт приз за текущий уровень (World-Stage). /// Конфигурация берётся из . /// public void GivePrizeForCurrentLevel() { string level = $"{GameManager.Instance.World}-{GameManager.Instance.Stage}"; GivePrizeForLevel(level); } /// /// Выдаёт приз за указанный уровень. /// public void GivePrizeForLevel(string levelName) { LevelPrizeConfig config = Database.GetConfig(levelName); if (config.type == PrizeType.None) { Debug.Log($"[PrizeSystemManager] Уровень «{levelName}»: приз не назначен."); OnAnyPrizeResult?.Invoke(PrizeType.None, true); return; } if (!_executors.TryGetValue(config.type, out PrizeBase executor)) { Debug.LogWarning($"[PrizeSystemManager] Нет исполнителя для типа «{config.type}»."); return; } if (executor.IsGiving) { Debug.LogWarning($"[PrizeSystemManager] Исполнитель «{config.type}» занят."); return; } Debug.Log($"[PrizeSystemManager] Уровень «{levelName}» → приз «{config.type}»."); executor.Execute(this, config); } /// /// Обратная совместимость: выдаёт приз хоппером (если включён в настройках). /// public void GivePrize() { bool hopperEnabled = SettingsManager.Instance != null && SettingsManager.Instance.GetBool("prizes.giveSmallPrize"); if (hopperEnabled && _executors.TryGetValue(PrizeType.Hopper, out PrizeBase executor)) executor.Execute(this, new LevelPrizeConfig { type = PrizeType.Hopper }); else Debug.Log("[PrizeSystemManager] Хоппер отключён в настройках."); } /// /// Выдаёт приз указанного типа с конфигурацией по умолчанию. /// public void GivePrize(PrizeType type) { if (!_executors.TryGetValue(type, out PrizeBase executor)) { Debug.LogWarning($"[PrizeSystemManager] Нет исполнителя для «{type}»."); return; } executor.Execute(this, new LevelPrizeConfig { type = type }); } /// Возвращает QR-коды (кешируется до перегенерации). public Texture2D[] GetQRCodes() { if (_qrCodesDirty || _qrCodesCache == null) { _qrCodesCache = _generatedQRCodes.ToArray(); _qrCodesDirty = false; } return _qrCodesCache; } /// Идёт ли выдача какого-либо приза. public bool IsAnyPrizeGiving { get { foreach (PrizeBase ex in _executors.Values) if (ex.IsGiving) return true; return false; } } // ────────────── Регистрация исполнителей ────────────── /// Регистрирует исполнитель приза. Заменяет предыдущий для того же типа. public void RegisterExecutor(PrizeBase executor) { _executors[executor.Type] = executor; executor.OnPrizeResult += success => OnAnyPrizeResult?.Invoke(executor.Type, success); } // ────────────── QR-коды (глобальная генерация) ────────────── private void RefreshPhoneNumber() { var settings = SettingsManager.Instance; _phoneNumber = settings?.GetString("prizes.phoneNumber") ?? string.Empty; _phoneNumber = PhoneNumberUtils.ValidatePhoneNumber(_phoneNumber); _phoneNumber = PhoneNumberUtils.ApplyPhoneMask(_phoneNumber, phoneNumberMask); } private void GenerateQRCodes() { _generatedQRCodes.Clear(); _qrCodesDirty = true; foreach (QRCodeInfo data in qrCodes) { if (!data.generateQRCode) continue; string fullLink = data.GetFullLink(_phoneNumber); Texture2D tex = QRMaker.MakeTexture( fullLink, 8, true, QRCodeInfo.GetColor(data.darkColor), QRCodeInfo.GetColor(data.lightColor)); _generatedQRCodes.Add(tex); } } // ────────────── Настройки ────────────── private void SubscribeToSettings() { var settings = SettingsManager.Instance; if (settings != null) settings.OnSettingChanged += OnSettingChanged; } private void UnsubscribeFromSettings() { var settings = SettingsManager.Instance; if (settings != null) settings.OnSettingChanged -= OnSettingChanged; } private void OnSettingChanged(string key, SettingValue value) { if (key == "prizes.phoneNumber") { RefreshPhoneNumber(); GenerateQRCodes(); Debug.Log("[PrizeSystemManager] QR-коды перегенерированы."); } } } }