Danro/Assets/Scripts/Managers/BalanceManager.cs
Danil Iarosh 32cfa79578 refactor: Refactor work with COM to ComSystemManager
- Добавлен ComSystemManager как новый публичный компонент COM-системы для поэтапной замены ComWrapper
- Реализовано получение роли контроллера только после успешного handshake в ComDeviceClient
- Добавлено публичное поле ControllerRole в ComDeviceClient
- Добавлен запрос роли по команде 97 и разбор ответа формата ROLE : <value> (с пробелами)
- В ComMultiManager добавлена логика выбора DefaultDeviceId по роли main_controller
- Добавлен доступ к роли устройства через GetDeviceRole(deviceId) в менеджере и фасаде
- В ComWrapper добавлены настройки роли: roleRequestCommand, roleResponsePrefix, roleRequestTimeoutMs, mainControllerRole
- Дефолтная команда запроса роли изменена на 97
- Выполнена кодовая миграция зависимостей с ComWrapper на ComSystemManager в GameManager, GlobalsManager, PaymentManager, BalanceManager, InactivityManager, COMInput, LightController, PrizeSystem
- В GlobalsManager исправлена инициализация ссылки после AddComponent<ComSystemManager>(), чтобы исключить NullReference
2026-02-20 14:59:16 +03:00

330 lines
12 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 RR.Common.Controls;
using RR.Common.Settings;
using UnityEngine;
using UnityEngine.Events;
namespace RR.Common.Managers
{
/// <summary>
/// Менеджер игрового баланса. Управляет внутриигровой валютой,
/// которая начисляется за импульсы оплаты и списывается за начало/продолжение игры.
/// </summary>
public class BalanceManager : MonoBehaviour
{
public static BalanceManager Instance { get; private set; }
[Header("Текущий баланс")]
[SerializeField] private int currentBalance;
[Header("Настройки сохранения")]
[SerializeField] private bool saveBalanceBetweenSessions;
// События для уведомления UI и других систем
public UnityEvent<int> OnBalanceChanged;
public UnityEvent<int, int> OnBalanceAdded; // (добавлено, новый баланс)
public UnityEvent<int, int> OnBalanceSpent; // (потрачено, новый баланс)
/// <summary>
/// Текущий баланс игрока
/// </summary>
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<int>();
OnBalanceChanged ??= new UnityEvent<int>();
OnBalanceAdded ??= new UnityEvent<int, int>();
OnBalanceSpent ??= new UnityEvent<int, int>();
// Загрузка баланса из сохранений
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;
}
}
/// <summary>
/// Кеширует значения из настроек
/// </summary>
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;
}
}
/// <summary>
/// Обработчик оплаты импульса - начисляет баланс
/// </summary>
private void OnImpulsePaid(int creditsCount)
{
// creditsCount - это количество добавленных кредитов (всегда 1 для одного импульса)
// Начисляем стоимость импульса
AddBalance(_impulseCost);
}
/// <summary>
/// Добавляет баланс игроку
/// </summary>
/// <param name="amount">Количество добавляемого баланса</param>
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);
}
/// <summary>
/// Проверяет, достаточно ли баланса для начала игры
/// </summary>
public bool CanStartGame()
{
// Проверка бесплатной игры
if (_freeGame)
return true;
return currentBalance >= _startGameCost;
}
/// <summary>
/// Проверяет, достаточно ли баланса для продолжения игры
/// </summary>
public bool CanContinueGame()
{
// Проверка бесплатной игры
if (_freeGame)
return true;
return currentBalance >= _continueGameCost;
}
/// <summary>
/// Списывает баланс за начало игры
/// </summary>
/// <returns>true если списание успешно</returns>
public bool SpendForStart()
{
// Проверка бесплатной игры
if (_freeGame)
return true;
return SpendBalance(_startGameCost, "начало игры");
}
/// <summary>
/// Списывает баланс за продолжение игры
/// </summary>
/// <returns>true если списание успешно</returns>
public bool SpendForContinue()
{
// Проверка бесплатной игры
if (_freeGame)
return true;
return SpendBalance(_continueGameCost, "продолжение игры");
}
/// <summary>
/// Списывает указанное количество баланса
/// </summary>
/// <param name="amount">Количество списываемого баланса</param>
/// <param name="reason">Причина списания (для логирования)</param>
/// <returns>true если списание успешно</returns>
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;
}
/// <summary>
/// Сбрасывает баланс до нуля
/// </summary>
public void ResetBalance()
{
currentBalance = 0;
if (saveBalanceBetweenSessions)
{
PlayerPrefs.SetInt(BALANCE_SAVE_KEY, 0);
PlayerPrefs.Save();
}
// Синхронизация кредитов с балансом
SyncCreditsWithBalance();
OnBalanceChanged?.Invoke(currentBalance);
}
/// <summary>
/// Синхронизирует кредиты в ComSystemManager с текущим балансом
/// по формуле: кредиты = баланс / стоимость_импульса (округление вниз)
/// </summary>
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; // Кредиты уже синхронизированы
// Устанавливаем кредиты напрямую через SetCredits
ComSystemManager.SetCredits(targetCredits);
}
/// <summary>
/// Устанавливает баланс напрямую (для тестирования/отладки)
/// </summary>
public void SetBalance(int amount)
{
currentBalance = Mathf.Max(0, amount);
if (saveBalanceBetweenSessions)
{
PlayerPrefs.SetInt(BALANCE_SAVE_KEY, currentBalance);
PlayerPrefs.Save();
}
// Синхронизация кредитов с балансом
SyncCreditsWithBalance();
OnBalanceChanged?.Invoke(currentBalance);
}
}
}