using System; using UnityEngine; using Cysharp.Threading.Tasks; namespace BulletHellTemplate { /// /// Maintains Battle-Pass XP, level and premium/reward flow. /// Server–side persistence is delegated to . /// Season state (number/start/end) is cached in . /// [DisallowMultipleComponent] public sealed class BattlePassManager : MonoBehaviour { public static BattlePassManager Singleton { get; private set; } public event EventHandler OnXPChanged; /*──────────── Runtime state ───────────*/ [HideInInspector] public int currentXP; [HideInInspector] public int currentLevel; [HideInInspector] public int xpForNextLevel; [Header("Translations")] [SerializeField] private string notEnoughFallback = "Not enough {1}. Need {0} more."; [SerializeField] private NameTranslatedByLanguage[] notEnoughTranslated; /*────────────────── Unity ──────────────*/ private void Awake() { if (Singleton == null) { Singleton = this; LoadProgress(); } else Destroy(gameObject); } /*───────────────── Public API ──────────*/ public bool IsPremium() => PlayerSave.CheckBattlePassPremiumUnlocked(); public bool IsRewardClaimed(string id) => PlayerSave.CheckBattlePassRewardClaimed(id); public bool HasSeasonEnded() => PlayerSave.HasSeasonEnded(); public int XPForLevel(int level) => Mathf.FloorToInt(GameInstance.Singleton.baseExpPass * Mathf.Pow(1f + GameInstance.Singleton.incPerLevelPass, level - 1)); /// Re-reads XP/level from and raises . public void SyncFromPlayerSave() => LoadProgress(); /// Returns the cached season number. public UniTask GetSeasonAsync() => UniTask.FromResult(Mathf.Max(1, PlayerSave.GetBattlePassCurrentSeason())); /// Returns remaining time until season end based on cached timestamps. public UniTask GetRemainingAsync() => UniTask.FromResult(PlayerSave.GetBattlePassSeasonRemaining()); /*──────────── Premium / Reward ────────*/ public async UniTask TryUnlockPremiumAsync() { RequestResult ok = await BackendManager.Service.UnlockBattlePassPremiumAsync(); if (ok.Success) { OnXPChanged?.Invoke(this, EventArgs.Empty); return null; } int balance = MonetizationManager.GetCurrency(GameInstance.Singleton.battlePassCurrencyID); string tpl = Translate(notEnoughTranslated, notEnoughFallback); return string.Format(tpl, GameInstance.Singleton.battlePassPrice - balance, GameInstance.Singleton.battlePassCurrencyID); } public async UniTask ClaimRewardAsync(BattlePassItem item) { var r = await BackendManager.Service.ClaimBattlePassRewardAsync(item); if (!r.Success) Debug.LogWarning(r.Reason); } /*──────────── Persistence ─────────────*/ private void LoadProgress() { (int xp, int lvl, bool _) = PlayerSave.GetBattlePassProgress(); currentXP = xp; currentLevel = lvl; xpForNextLevel = XPForLevel(lvl); OnXPChanged?.Invoke(this, EventArgs.Empty); } /*──────────── Translation helper ──────*/ private static string Translate(NameTranslatedByLanguage[] table, string fallback) { string lang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage(); foreach (var t in table) if (t.LanguageId.Equals(lang, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(t.Translate)) return t.Translate; return fallback; } } }