198 lines
9.3 KiB
C#
198 lines
9.3 KiB
C#
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;
|
||
|
||
// ── Shared balance ────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// true — включён режим общего баланса: оба игрока видят единый пул кредитов.
|
||
/// </summary>
|
||
public static bool IsSharedBalance()
|
||
=> SettingsManager.instance != null &&
|
||
SettingsManager.instance.GetSettingValue("Общий баланс") == "Вкл";
|
||
|
||
// ── 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);
|
||
|
||
// При общем балансе синхронизируем — источник истины Player1_Coins
|
||
if (IsSharedBalance())
|
||
{
|
||
_creditsPlayer2 = _creditsPlayer1;
|
||
PlayerPrefs.SetInt("Player2_Coins", _creditsPlayer2);
|
||
}
|
||
|
||
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 (IsSharedBalance())
|
||
{
|
||
// Единый пул — пишем в Player1, синхронизируем Player2, уведомляем оба UI
|
||
_creditsPlayer1 += coins;
|
||
_creditsPlayer2 = _creditsPlayer1;
|
||
PlayerPrefs.SetInt("Player1_Coins", _creditsPlayer1);
|
||
PlayerPrefs.SetInt("Player2_Coins", _creditsPlayer2);
|
||
onCoinAdded?.Invoke(1, _creditsPlayer1);
|
||
onCoinAdded?.Invoke(2, _creditsPlayer2);
|
||
return;
|
||
}
|
||
|
||
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;
|
||
|
||
if (IsSharedBalance())
|
||
{
|
||
// Единый пул — списываем из Player1, синхронизируем Player2, уведомляем оба UI
|
||
if (_creditsPlayer1 >= coins)
|
||
{
|
||
_creditsPlayer1 -= coins;
|
||
_creditsPlayer2 = _creditsPlayer1;
|
||
PlayerPrefs.SetInt("Player1_Coins", _creditsPlayer1);
|
||
PlayerPrefs.SetInt("Player2_Coins", _creditsPlayer2);
|
||
onCoinRemoved?.Invoke(1, _creditsPlayer1);
|
||
onCoinRemoved?.Invoke(2, _creditsPlayer2);
|
||
return true;
|
||
}
|
||
OnInsufficientFunds?.Invoke(playerId);
|
||
return false;
|
||
}
|
||
|
||
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)
|
||
{
|
||
if (IsSharedBalance()) return _creditsPlayer1;
|
||
return playerId == 1 ? _creditsPlayer1 : _creditsPlayer2;
|
||
}
|
||
}
|
||
|