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 bool _isSyncingCredits;
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)
{
if (_isSyncingCredits)
return;
// 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);
}
///
/// Синхронизирует кредиты в ComSystemManager с текущим балансом
/// по формуле: кредиты = баланс / стоимость_импульса (округление вниз)
///
private void SyncCreditsWithBalance()
{
if (_impulseCost <= 0)
{
Debug.LogError("[BalanceManager] Cannot sync credits - invalid impulse cost");
return;
}
int targetCredits = currentBalance / _impulseCost;
int currentCredits = ComSystemManager.GetCredits();
if (targetCredits == currentCredits)
return; // Кредиты уже синхронизированы
_isSyncingCredits = true;
try
{
// Устанавливаем кредиты напрямую через SetCredits
ComSystemManager.SetCredits(targetCredits);
}
finally
{
_isSyncingCredits = false;
}
}
///
/// Устанавливает баланс напрямую (для тестирования/отладки)
///
public void SetBalance(int amount)
{
currentBalance = Mathf.Max(0, amount);
if (saveBalanceBetweenSessions)
{
PlayerPrefs.SetInt(BALANCE_SAVE_KEY, currentBalance);
PlayerPrefs.Save();
}
// Синхронизация кредитов с балансом
SyncCreditsWithBalance();
OnBalanceChanged?.Invoke(currentBalance);
}
}
}