192 lines
6.4 KiB
C#
192 lines
6.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace BulletHellTemplate
|
|
{
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
public sealed class RewardManagerPopup : MonoBehaviour
|
|
{
|
|
public static RewardManagerPopup Singleton;
|
|
|
|
// Maps completed this session but not yet claimed.
|
|
private readonly HashSet<int> _completedSession = new();
|
|
|
|
private List<int> _claimed = new();
|
|
private bool _claimedLoaded = false;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Singleton == null)
|
|
{
|
|
Singleton = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
MergeServerClaimed(serverClaimedMapIds);
|
|
StartCoroutine(WaitAndShowRewards());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Re-show pending rewards using current local state (no merge).
|
|
/// </summary>
|
|
public void StartSyncFromServer() => StartCoroutine(WaitAndShowRewards());
|
|
|
|
/// <summary>
|
|
/// Call when the player successfully completes a map.
|
|
/// If that map is rewardable and not already claimed, it becomes pending for this session.
|
|
/// </summary>
|
|
public void RegisterMapCompleted(int mapId)
|
|
{
|
|
var mapData = GetMapDataById(mapId);
|
|
if (mapData == null)
|
|
{
|
|
Debug.LogError($"[RewardManagerPopup] Map data not found (mapId:{mapId}).");
|
|
return;
|
|
}
|
|
|
|
if (!ShouldReward(mapData))
|
|
return; // not rewardable; ignore
|
|
|
|
if (HasLocalMapClaimed(mapId))
|
|
return; // already claimed; ignore
|
|
|
|
if (_completedSession.Add(mapId))
|
|
{
|
|
Debug.Log($"[RewardManagerPopup] Map {mapId} marked completed (pending reward).");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if the specified map reward is already claimed (local persisted state).
|
|
/// </summary>
|
|
public bool HasLocalMapClaimed(int mapId)
|
|
{
|
|
EnsureClaimedLoaded();
|
|
return _claimed.Contains(mapId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps completed this session and not yet claimed.
|
|
/// </summary>
|
|
public List<int> GetLocalUnclaimedMaps()
|
|
{
|
|
EnsureClaimedLoaded();
|
|
var list = new List<int>();
|
|
foreach (var id in _completedSession)
|
|
{
|
|
if (!_claimed.Contains(id))
|
|
list.Add(id);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mark a map's reward as successfully claimed.
|
|
/// Removes from session pending and persists in PlayerSave.
|
|
/// </summary>
|
|
public void MarkRewardClaimed(int mapId)
|
|
{
|
|
EnsureClaimedLoaded();
|
|
if (!_claimed.Contains(mapId))
|
|
{
|
|
_claimed.Add(mapId);
|
|
PlayerSave.SaveClaimedMapRewards(_claimed);
|
|
}
|
|
_completedSession.Remove(mapId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Merge server-authoritative claimed list into local persisted claimed list (union).
|
|
/// </summary>
|
|
private void MergeServerClaimed(List<int> serverClaimed)
|
|
{
|
|
EnsureClaimedLoaded();
|
|
|
|
if (serverClaimed != null && serverClaimed.Count > 0)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure claimed cache is loaded from PlayerSave once.
|
|
/// </summary>
|
|
private void EnsureClaimedLoaded()
|
|
{
|
|
if (_claimedLoaded) return;
|
|
_claimed = PlayerSave.LoadClaimedMapRewards() ?? new List<int>();
|
|
_claimedLoaded = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wait until UIRewardManagerPopup exists; then signal it to show pending rewards.
|
|
/// </summary>
|
|
private IEnumerator WaitAndShowRewards()
|
|
{
|
|
while (UIRewardManagerPopup.Singleton == null)
|
|
yield return null;
|
|
|
|
yield return null; // one frame for UI init
|
|
UIRewardManagerPopup.Singleton.ShowPendingRewards();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lookup map definition from GameInstance.
|
|
/// </summary>
|
|
private MapInfoData GetMapDataById(int mapId)
|
|
{
|
|
var maps = GameInstance.Singleton?.mapInfoData;
|
|
if (maps == null) return null;
|
|
return Array.Find(maps, m => m != null && m.mapId == mapId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if the map is configured to award a first-time completion reward and has reward content.
|
|
/// </summary>
|
|
private bool ShouldReward(MapInfoData mapData)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|