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