using RR.Common.Controls; using RR.Common.Settings; using UnityEngine; using UnityEngine.Events; namespace RR.Common.Managers { /// /// Менеджер игрового баланса. Управляет внутриигровой валютой, /// которая начисляется за импульсы оплаты и списывается за начало/продолжение игры. /// public class BalanceManager : MonoBehaviour { public static BalanceManager Instance { get; private set; } [Header("Текущий баланс")] [SerializeField] private int currentBalance; [Header("Настройки сохранения")] [SerializeField] private bool saveBalanceBetweenSessions; // События для уведомления UI и других систем public UnityEvent OnBalanceChanged; public UnityEvent OnBalanceAdded; // (добавлено, новый баланс) public UnityEvent OnBalanceSpent; // (потрачено, новый баланс) /// /// Текущий баланс игрока /// public int CurrentBalance => currentBalance; private const string BALANCE_SAVE_KEY = "PlayerBalance"; // Кешированные значения из настроек private int _impulseCost = 10; private int _startGameCost = 10; private int _continueGameCost = 5; private bool _freeGame; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); Initialize(); } private void Initialize() { // Инициализация событий OnBalanceChanged ??= new UnityEvent(); OnBalanceChanged ??= new UnityEvent(); OnBalanceAdded ??= new UnityEvent(); OnBalanceSpent ??= new UnityEvent(); // Загрузка баланса из сохранений if (saveBalanceBetweenSessions) { currentBalance = PlayerPrefs.GetInt(BALANCE_SAVE_KEY, 0); } else { currentBalance = 0; PlayerPrefs.SetInt(BALANCE_SAVE_KEY, 0); } // Подписка на события настроек для кеширования значений var settingsManager = SettingsManager.Instance; if (settingsManager != null) { settingsManager.OnSettingsLoaded += CacheSettingsValues; settingsManager.OnSettingsSaved += CacheSettingsValues; CacheSettingsValues(); // Загрузка при инициализации } // Подписка на события оплаты if (PaymentManager.Instance != null) { PaymentManager.Instance.OnCreditAdded.AddListener(OnImpulsePaid); } Debug.Log($"[BalanceManager] Инициализирован. Баланс: {currentBalance}"); } private void OnDestroy() { if (PaymentManager.Instance != null) { PaymentManager.Instance.OnCreditAdded.RemoveListener(OnImpulsePaid); } var settingsManager = SettingsManager.Instance; if (settingsManager != null) { settingsManager.OnSettingsLoaded -= CacheSettingsValues; settingsManager.OnSettingsSaved -= CacheSettingsValues; } } /// /// Кеширует значения из настроек /// private void CacheSettingsValues() { var settings = SettingsManager.Instance; if (settings == null) { Debug.LogError("[BalanceManager] SettingsManager is null, cannot cache settings!"); return; } _impulseCost = settings.GetInt("payment.paymentImpulseCost"); _startGameCost = settings.GetInt("payment.startGameCost"); _continueGameCost = settings.GetInt("payment.continueGameCost"); _freeGame = settings.GetBool("payment.freeGame"); // Валидация значений if (_impulseCost <= 0) { Debug.LogError($"[BalanceManager] Invalid payment.paymentImpulseCost: {_impulseCost}"); _impulseCost = 10; } if (_startGameCost <= 0) { Debug.LogError($"[BalanceManager] Invalid payment.startGameCost: {_startGameCost}"); _startGameCost = 10; } if (_continueGameCost <= 0) { Debug.LogError($"[BalanceManager] Invalid payment.continueGameCost: {_continueGameCost}"); _continueGameCost = 5; } } /// /// Обработчик оплаты импульса - начисляет баланс /// private void OnImpulsePaid(int creditsCount) { // creditsCount - это количество добавленных кредитов (всегда 1 для одного импульса) // Начисляем стоимость импульса AddBalance(_impulseCost); } /// /// Добавляет баланс игроку /// /// Количество добавляемого баланса public void AddBalance(int amount) { if (amount <= 0) { Debug.LogWarning($"[BalanceManager] Trying to add invalid amount: {amount}"); return; } currentBalance += amount; // Сохранение если включено if (saveBalanceBetweenSessions) { PlayerPrefs.SetInt(BALANCE_SAVE_KEY, currentBalance); PlayerPrefs.Save(); } // Синхронизация кредитов с балансом SyncCreditsWithBalance(); // Уведомление подписчиков OnBalanceAdded?.Invoke(amount, currentBalance); OnBalanceChanged?.Invoke(currentBalance); } /// /// Проверяет, достаточно ли баланса для начала игры /// public bool CanStartGame() { // Проверка бесплатной игры if (_freeGame) return true; return currentBalance >= _startGameCost; } /// /// Проверяет, достаточно ли баланса для продолжения игры /// public bool CanContinueGame() { // Проверка бесплатной игры if (_freeGame) return true; return currentBalance >= _continueGameCost; } /// /// Списывает баланс за начало игры /// /// true если списание успешно public bool SpendForStart() { // Проверка бесплатной игры if (_freeGame) return true; return SpendBalance(_startGameCost, "начало игры"); } /// /// Списывает баланс за продолжение игры /// /// true если списание успешно public bool SpendForContinue() { // Проверка бесплатной игры if (_freeGame) return true; return SpendBalance(_continueGameCost, "продолжение игры"); } /// /// Списывает указанное количество баланса /// /// Количество списываемого баланса /// Причина списания (для логирования) /// true если списание успешно public bool SpendBalance(int amount, string reason = "") { if (amount <= 0) { Debug.LogWarning($"[BalanceManager] Trying to spend invalid amount: {amount}"); return false; } if (currentBalance < amount) { return false; } currentBalance -= amount; // Сохранение если включено if (saveBalanceBetweenSessions) { PlayerPrefs.SetInt(BALANCE_SAVE_KEY, currentBalance); PlayerPrefs.Save(); } // Синхронизация кредитов с балансом SyncCreditsWithBalance(); // Уведомление подписчиков OnBalanceSpent?.Invoke(amount, currentBalance); OnBalanceChanged?.Invoke(currentBalance); return true; } /// /// Сбрасывает баланс до нуля /// public void ResetBalance() { currentBalance = 0; if (saveBalanceBetweenSessions) { PlayerPrefs.SetInt(BALANCE_SAVE_KEY, 0); PlayerPrefs.Save(); } // Синхронизация кредитов с балансом SyncCreditsWithBalance(); OnBalanceChanged?.Invoke(currentBalance); } /// /// Синхронизирует кредиты в ComWrapper с текущим балансом /// по формуле: кредиты = баланс / стоимость_импульса (округление вниз) /// private void SyncCreditsWithBalance() { if (_impulseCost <= 0) { Debug.LogError("[BalanceManager] Cannot sync credits - invalid impulse cost"); return; } int targetCredits = currentBalance / _impulseCost; int currentCredits = ComWrapper.GetCredits(); if (targetCredits == currentCredits) return; // Кредиты уже синхронизированы // Устанавливаем кредиты напрямую через SetCredits ComWrapper.SetCredits(targetCredits); } /// /// Устанавливает баланс напрямую (для тестирования/отладки) /// public void SetBalance(int amount) { currentBalance = Mathf.Max(0, amount); if (saveBalanceBetweenSessions) { PlayerPrefs.SetInt(BALANCE_SAVE_KEY, currentBalance); PlayerPrefs.Save(); } // Синхронизация кредитов с балансом SyncCreditsWithBalance(); OnBalanceChanged?.Invoke(currentBalance); } } }