using System; using RR.Game2D.Core.Managers; using UnityEngine; namespace RR.Game2D.Behaviour { /// /// Система очков: /// хранит количество собранных монет и убитых врагов, /// считает итоговый счет при завершении игры. /// Формула: Score = E * (P + 45C + 90K + 10K^2) - 0.5T /// [DefaultExecutionOrder(-900)] public class ScoreSystem : MonoBehaviour { public static ScoreSystem Instance { get; private set; } [Header("Коэффициенты исхода")] [SerializeField] private float winMultiplier = 1f; [SerializeField] private float loseMultiplier = 0.6f; public int CollectedCoins { get; private set; } public int TotalCollectedCoins => CollectedCoins; public int KilledEnemies { get; private set; } public int EnvironmentalPoints { get; private set; } public int FinalScore { get; private set; } public bool IsFinalized { get; private set; } public event Action StatsChanged; public event Action EnvironmentalPointsChanged; public event Action ScoreCalculated; private bool _subscribed; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void Bootstrap() { if (Instance != null) return; GameObject go = new(nameof(ScoreSystem)); go.AddComponent(); DontDestroyOnLoad(go); } private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); ResetStats(); } private void Start() { TrySubscribe(); } private void OnEnable() { TrySubscribe(); } private void OnDisable() { Unsubscribe(); } public void RegisterCoinCollected(int amount = 1) { if (amount <= 0 || IsFinalized) return; // This is the cumulative coin count for score calculation. // It is intentionally separate from GameManager.Coins, which can wrap at 100 to award an extra life. CollectedCoins += amount; StatsChanged?.Invoke(CollectedCoins, KilledEnemies); } public void RegisterEnemyKilled(int amount = 1) { if (amount <= 0 || IsFinalized) return; KilledEnemies += amount; StatsChanged?.Invoke(CollectedCoins, KilledEnemies); } public void RegisterEnvironmentalPoints(int amount = 1) { if (amount <= 0 || IsFinalized) return; EnvironmentalPoints += amount; EnvironmentalPointsChanged?.Invoke(EnvironmentalPoints); } public void ResetStats() { CollectedCoins = 0; KilledEnemies = 0; EnvironmentalPoints = 0; FinalScore = 0; IsFinalized = false; StatsChanged?.Invoke(CollectedCoins, KilledEnemies); EnvironmentalPointsChanged?.Invoke(EnvironmentalPoints); } private void TrySubscribe() { if (_subscribed || GameManager.Instance == null) return; GameManager.Instance.GameWonEvent += OnGameWon; GameManager.Instance.GameOverEvent += OnGameOver; _subscribed = true; } private void Unsubscribe() { if (!_subscribed || GameManager.Instance == null) return; GameManager.Instance.GameWonEvent -= OnGameWon; GameManager.Instance.GameOverEvent -= OnGameOver; _subscribed = false; } private void OnGameWon(float gameTime) { Calculate(gameTime, true); } private void OnGameOver(float gameTime) { Calculate(gameTime, false); } private void Calculate(float gameTime, bool isWin) { if (IsFinalized) return; float e = isWin ? winMultiplier : loseMultiplier; float baseScore = EnvironmentalPoints + 45f * CollectedCoins + 90f * KilledEnemies + 10f * KilledEnemies * KilledEnemies; float rawScore = e * baseScore - 0.5f * gameTime; FinalScore = Mathf.Max(0, Mathf.RoundToInt(rawScore)); IsFinalized = true; ScoreCalculated?.Invoke(FinalScore, gameTime, isWin); } } }