sea-battle/Assets/Scripts/CreditSystem.cs

152 lines
7.2 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 UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Хранит и управляет балансом кредитов обоих игроков.
/// Инициализируется всегда — независимо от режима ввода (клавиатура или COM).
/// COM-плата пишет монеты через CreditSystem.AddImpulse(), а не напрямую в UI.
/// </summary>
public class CreditSystem : MonoBehaviour
{
[SerializeField] private bool saveMoneyBetweenSessions;
// ── Events ───────────────────────────────────────────────────────────────
public static UnityEvent<int, int> onCoinAdded;
public static UnityEvent<int, int> onCoinRemoved;
public static System.Action OnInitialize;
public static event System.Action<int> OnInsufficientFunds;
// ── Private data ──────────────────────────────────────────────────────────
private static int _creditsPlayer1;
private static int _creditsPlayer2;
// ── Prices (read from PlayerPrefs, same keys as AdminContactsController) ──
/// <summary>Сколько кредитов даёт один физический импульс (монета). Минимум 1.</summary>
public static int ImpulsePrice => ParsePositive(PlayerPrefs.GetString("ImpulsePrice", "1"), 1);
/// <summary>Стоимость игры с ботом в кредитах. Минимум 1.</summary>
public static int BotGamePrice => ParsePositive(PlayerPrefs.GetString("PriceBotGame", "1"), 1);
/// <summary>Стоимость игры с другом в кредитах. Минимум 1.</summary>
public static int PlayerGamePrice => ParsePositive(PlayerPrefs.GetString("PricePlayerGame", "1"), 1);
/// <summary>Цена реванша с ботом — рассчитывается через скидку из настроек.</summary>
[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<int, int>();
onCoinRemoved = new UnityEvent<int, int>();
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 ────────────────────────────────────────────────────────────
/// <summary>
/// Пополняет баланс на один физический импульс (монету):
/// добавляет ImpulsePrice кредитов и считает монету в счётчике призовой системы.
/// Вызывается из ComWrapper и Player.cs при приёме монеты.
/// </summary>
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);
}
/// <summary>
/// Добавляет точное количество кредитов (используется для возвратов/рефандов).
/// НЕ трогает счётчики призовой системы.
/// </summary>
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);
}
}
/// <summary>Списывает кредиты за старт игры. Возвращает false если денег нет.</summary>
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;
}
/// <summary>
/// Возвращает эффективную стоимость игры с ботом с учётом скидки за реванш.
/// Используется и для списания (PayBotGame) и для проверки баланса в UI.
/// </summary>
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;
}
/// <summary>
/// Списывает кредиты за игру с ботом.
/// Если isReplay=true и в настройках включена скидка — применяет % скидку.
/// </summary>
public static bool PayBotGame(bool isReplay, int playerId)
{
return Pay(GetEffectiveBotPrice(isReplay), playerId);
}
/// <summary>Возвращает текущий баланс игрока.</summary>
public static int GetPlayerCredits(int playerId = 1)
{
return playerId == 1 ? _creditsPlayer1 : _creditsPlayer2;
}
}