192 lines
6.4 KiB
C#
Raw Permalink Normal View History

2025-09-19 14:56:58 +05:00
using System;
using System.Collections;
2025-09-19 19:43:49 +05:00
using System.Collections.Generic;
using UnityEngine;
2025-09-19 14:56:58 +05:00
namespace BulletHellTemplate
{
/// <summary>
2025-09-19 19:43:49 +05:00
/// Central in-session tracker for "first completion" map rewards.
///
/// Responsibilities:
/// - Track which maps were *completed this session* (pending reward).
/// - Track which map rewards are *already claimed* (persistent via PlayerSave).
/// - Merge server-authoritative claimed list on startup.
/// - Provide pending list for UIRewardManagerPopup.
/// - Mark maps claimed after successful server claim.
///
/// No per-map PlayerPrefs keys are used (legacy removed).
/// Claimed persistence lives in PlayerSave.SaveClaimedMapRewards().
/// Pending completions are session-only (lost on app restart if not claimed).
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
public sealed class RewardManagerPopup : MonoBehaviour
2025-09-19 14:56:58 +05:00
{
public static RewardManagerPopup Singleton;
2025-09-19 19:43:49 +05:00
// Maps completed this session but not yet claimed.
private readonly HashSet<int> _completedSession = new();
2025-09-19 14:56:58 +05:00
2025-09-19 19:43:49 +05:00
private List<int> _claimed = new();
private bool _claimedLoaded = false;
2025-09-19 14:56:58 +05:00
private void Awake()
{
if (Singleton == null)
{
Singleton = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
2025-09-19 19:43:49 +05:00
/// <summary>
/// Merge server claimed mapIds (from /auth/init) into local persistent list,
/// then show pending rewards (if any).
/// </summary>
public void StartSyncFromServer(List<int> serverClaimedMapIds)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
MergeServerClaimed(serverClaimedMapIds);
StartCoroutine(WaitAndShowRewards());
2025-09-19 14:56:58 +05:00
}
2025-09-19 19:43:49 +05:00
/// <summary>
/// Re-show pending rewards using current local state (no merge).
/// </summary>
public void StartSyncFromServer() => StartCoroutine(WaitAndShowRewards());
2025-09-19 14:56:58 +05:00
/// <summary>
2025-09-19 19:43:49 +05:00
/// Call when the player successfully completes a map.
/// If that map is rewardable and not already claimed, it becomes pending for this session.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
public void RegisterMapCompleted(int mapId)
2025-09-19 14:56:58 +05:00
{
var mapData = GetMapDataById(mapId);
if (mapData == null)
{
2025-09-19 19:43:49 +05:00
Debug.LogError($"[RewardManagerPopup] Map data not found (mapId:{mapId}).");
2025-09-19 14:56:58 +05:00
return;
}
2025-09-19 19:43:49 +05:00
if (!ShouldReward(mapData))
return; // not rewardable; ignore
2025-09-19 14:56:58 +05:00
2025-09-19 19:43:49 +05:00
if (HasLocalMapClaimed(mapId))
return; // already claimed; ignore
2025-09-19 14:56:58 +05:00
2025-09-19 19:43:49 +05:00
if (_completedSession.Add(mapId))
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
Debug.Log($"[RewardManagerPopup] Map {mapId} marked completed (pending reward).");
2025-09-19 14:56:58 +05:00
}
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// True if the specified map reward is already claimed (local persisted state).
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
public bool HasLocalMapClaimed(int mapId)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
EnsureClaimedLoaded();
return _claimed.Contains(mapId);
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Maps completed this session and not yet claimed.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
public List<int> GetLocalUnclaimedMaps()
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
EnsureClaimedLoaded();
var list = new List<int>();
foreach (var id in _completedSession)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
if (!_claimed.Contains(id))
list.Add(id);
2025-09-19 14:56:58 +05:00
}
2025-09-19 19:43:49 +05:00
return list;
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Mark a map's reward as successfully claimed.
/// Removes from session pending and persists in PlayerSave.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
public void MarkRewardClaimed(int mapId)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
EnsureClaimedLoaded();
if (!_claimed.Contains(mapId))
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
_claimed.Add(mapId);
PlayerSave.SaveClaimedMapRewards(_claimed);
2025-09-19 14:56:58 +05:00
}
2025-09-19 19:43:49 +05:00
_completedSession.Remove(mapId);
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Merge server-authoritative claimed list into local persisted claimed list (union).
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
private void MergeServerClaimed(List<int> serverClaimed)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
EnsureClaimedLoaded();
2025-09-19 14:56:58 +05:00
2025-09-19 19:43:49 +05:00
if (serverClaimed != null && serverClaimed.Count > 0)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
bool changed = false;
foreach (var id in serverClaimed)
{
if (!_claimed.Contains(id))
{
_claimed.Add(id);
changed = true;
}
_completedSession.Remove(id);
}
if (changed)
PlayerSave.SaveClaimedMapRewards(_claimed);
2025-09-19 14:56:58 +05:00
}
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Ensure claimed cache is loaded from PlayerSave once.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
private void EnsureClaimedLoaded()
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
if (_claimedLoaded) return;
_claimed = PlayerSave.LoadClaimedMapRewards() ?? new List<int>();
_claimedLoaded = true;
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Wait until UIRewardManagerPopup exists; then signal it to show pending rewards.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
private IEnumerator WaitAndShowRewards()
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
while (UIRewardManagerPopup.Singleton == null)
yield return null;
yield return null; // one frame for UI init
UIRewardManagerPopup.Singleton.ShowPendingRewards();
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// Lookup map definition from GameInstance.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
private MapInfoData GetMapDataById(int mapId)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
var maps = GameInstance.Singleton?.mapInfoData;
if (maps == null) return null;
return Array.Find(maps, m => m != null && m.mapId == mapId);
2025-09-19 14:56:58 +05:00
}
/// <summary>
2025-09-19 19:43:49 +05:00
/// True if the map is configured to award a first-time completion reward and has reward content.
2025-09-19 14:56:58 +05:00
/// </summary>
2025-09-19 19:43:49 +05:00
private bool ShouldReward(MapInfoData mapData)
2025-09-19 14:56:58 +05:00
{
2025-09-19 19:43:49 +05:00
if (mapData == null) return false;
bool hasList = mapData.WinMapRewards != null && mapData.WinMapRewards.Count > 0;
bool hasSpecial = mapData.rewardType != MapRewardType.None;
return mapData.isRewardOnCompleteFirstTime && (hasList || hasSpecial);
2025-09-19 14:56:58 +05:00
}
}
}