using UnityEngine; using UnityEngine.Events; /// /// Хранит и управляет балансом кредитов обоих игроков. /// Инициализируется всегда — независимо от режима ввода (клавиатура или COM). /// COM-плата пишет монеты через CreditSystem.AddImpulse(), а не напрямую в UI. /// public class CreditSystem : MonoBehaviour { [SerializeField] private bool saveMoneyBetweenSessions; // ── Events ─────────────────────────────────────────────────────────────── public static UnityEvent onCoinAdded; public static UnityEvent onCoinRemoved; public static System.Action OnInitialize; public static event System.Action OnInsufficientFunds; // ── Private data ────────────────────────────────────────────────────────── private static int _creditsPlayer1; private static int _creditsPlayer2; // ── Prices (read from PlayerPrefs, same keys as AdminContactsController) ── /// Сколько кредитов даёт один физический импульс (монета). Минимум 1. public static int ImpulsePrice => ParsePositive(PlayerPrefs.GetString("ImpulsePrice", "1"), 1); /// Стоимость игры с ботом в кредитах. Минимум 1. public static int BotGamePrice => ParsePositive(PlayerPrefs.GetString("PriceBotGame", "1"), 1); /// Стоимость игры с другом в кредитах. Минимум 1. public static int PlayerGamePrice => ParsePositive(PlayerPrefs.GetString("PricePlayerGame", "1"), 1); /// Цена реванша с ботом — рассчитывается через скидку из настроек. [System.Obsolete("Используй PayBotGame(isReplay, playerId) — он сам считает скидку из настроек.")] public static int BotReplayPrice => Mathf.Max(1, BotGamePrice / 2); private static int ParsePositive(string s, int fallback) => (int.TryParse(s, out int r) && r > 0) ? r : fallback; // ── Init ────────────────────────────────────────────────────────────────── public void Initialize() { onCoinAdded = new UnityEvent(); onCoinRemoved = new UnityEvent(); if (!saveMoneyBetweenSessions) { PlayerPrefs.SetInt("Player1_Coins", 0); PlayerPrefs.SetInt("Player2_Coins", 0); } _creditsPlayer1 = PlayerPrefs.GetInt("Player1_Coins", 0); _creditsPlayer2 = PlayerPrefs.GetInt("Player2_Coins", 0); OnInitialize?.Invoke(); } // ── Public API ──────────────────────────────────────────────────────────── /// /// Пополняет баланс на один физический импульс (монету): /// добавляет ImpulsePrice кредитов и считает монету в счётчике призовой системы. /// Вызывается из ComWrapper и Player.cs при приёме монеты. /// public static void AddImpulse(int playerId = 1) { AddCredit(ImpulsePrice, playerId); // Счётчик физических монет для призовой системы PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1); PlayerPrefs.SetInt(PrizeSystem.key_startsNul, PlayerPrefs.GetInt(PrizeSystem.key_startsNul) + 1); } /// /// Добавляет точное количество кредитов (используется для возвратов/рефандов). /// НЕ трогает счётчики призовой системы. /// public static void AddCredit(int coins = 1, int playerId = 1) { if (playerId == 1) { _creditsPlayer1 += coins; PlayerPrefs.SetInt("Player1_Coins", _creditsPlayer1); onCoinAdded?.Invoke(playerId, _creditsPlayer1); } else if (playerId == 2) { _creditsPlayer2 += coins; PlayerPrefs.SetInt("Player2_Coins", _creditsPlayer2); onCoinAdded?.Invoke(playerId, _creditsPlayer2); } } /// Списывает кредиты за старт игры. Возвращает false если денег нет. public static bool Pay(int coins = 1, int playerId = 1) { if (SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл") return true; ref int credits = ref (playerId == 1 ? ref _creditsPlayer1 : ref _creditsPlayer2); string key = playerId == 1 ? "Player1_Coins" : "Player2_Coins"; if (credits >= coins) { credits -= coins; PlayerPrefs.SetInt(key, credits); onCoinRemoved?.Invoke(playerId, credits); return true; } OnInsufficientFunds?.Invoke(playerId); return false; } /// /// Возвращает эффективную стоимость игры с ботом с учётом скидки за реванш. /// Используется и для списания (PayBotGame) и для проверки баланса в UI. /// public static int GetEffectiveBotPrice(bool isReplay) { int price = BotGamePrice; if (isReplay && SettingsManager.instance != null) { bool discountOn = SettingsManager.instance.GetSettingValue("Скидка за реванш при игре с ботом") == "Вкл"; if (discountOn) { string pctStr = SettingsManager.instance.GetSettingValue("Размер скидки за реванш"); if (int.TryParse(pctStr, out int pct) && pct > 0 && pct < 100) price = Mathf.Max(1, price - price * pct / 100); } } return price; } /// /// Списывает кредиты за игру с ботом. /// Если isReplay=true и в настройках включена скидка — применяет % скидку. /// public static bool PayBotGame(bool isReplay, int playerId) { return Pay(GetEffectiveBotPrice(isReplay), playerId); } /// Возвращает текущий баланс игрока. public static int GetPlayerCredits(int playerId = 1) { return playerId == 1 ? _creditsPlayer1 : _creditsPlayer2; } }