Files
bansonic_beta_main/Assets/scripts/_runtimecache/ExpBottleLedger.cs
T

484 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
public sealed class ExpBottleLedger : MonoBehaviour
{
public static ExpBottleLedger Instance { get; private set; }
public event Action<ExpBottleKind, int> OnBottleCountChanged;
public event Action OnLedgerReloaded;
private readonly Dictionary<string, int> countsByKey = new Dictionary<string, int>(StringComparer.Ordinal);
private bool initialized;
private bool loadedFromSave;
private Player_SO boundPlayerData;
public bool IsReady
{
get { return initialized; }
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
EnsureInstance();
}
public static ExpBottleLedger EnsureInstance()
{
if (Instance != null)
{
return Instance;
}
var host = new GameObject("__runtime_cache_bridge");
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
DontDestroyOnLoad(host);
Instance = host.AddComponent<ExpBottleLedger>();
return Instance;
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeIfNeeded();
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
SaveNow();
}
}
private void OnApplicationQuit()
{
SaveNow();
}
public void InitializeIfNeeded()
{
if (initialized)
{
return;
}
ExpBottleLedgerPayload payload;
loadedFromSave = ExpBottleLedgerStorage.TryLoad(out payload);
RebuildFromPayload(payload);
initialized = true;
TryRecoverFromDefaultPlayerData();
SyncMirrorCounts();
SaveNow();
if (OnLedgerReloaded != null)
{
OnLedgerReloaded();
}
}
public void AttachPlayerData(Player_SO playerData)
{
if (playerData == null)
{
return;
}
InitializeIfNeeded();
boundPlayerData = playerData;
SyncToPlayerData();
}
public int GetCount(ExpBottleKind kind)
{
return GetCountByKey(ExpBottleCatalog.GetKey(kind));
}
public int GetCountByKey(string key)
{
InitializeIfNeeded();
int count;
if (!countsByKey.TryGetValue(key, out count))
{
return 0;
}
return count;
}
public IReadOnlyList<ExpBottleSnapshotEntry> GetSnapshot()
{
InitializeIfNeeded();
var result = new List<ExpBottleSnapshotEntry>(ExpBottleCatalog.All.Count);
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
result.Add(new ExpBottleSnapshotEntry
{
key = descriptor.Key,
count = GetCountByKey(descriptor.Key)
});
}
return result;
}
public void Add(ExpBottleKind kind, int amount)
{
if (amount <= 0)
{
return;
}
ChangeCount(ExpBottleCatalog.GetKey(kind), amount);
}
public void AddByKey(string key, int amount)
{
if (string.IsNullOrWhiteSpace(key) || amount <= 0)
{
return;
}
ChangeCount(key.Trim(), amount);
}
public bool TryConsume(ExpBottleKind kind, int amount)
{
if (amount <= 0)
{
return false;
}
var key = ExpBottleCatalog.GetKey(kind);
var current = GetCountByKey(key);
if (current < amount)
{
return false;
}
ChangeCount(key, -amount);
DailyTaskEventHub.ReportUseItem(amount);
return true;
}
public bool TryConsumeByKey(string key, int amount)
{
if (string.IsNullOrWhiteSpace(key) || amount <= 0)
{
return false;
}
var safeKey = key.Trim();
var current = GetCountByKey(safeKey);
if (current < amount)
{
return false;
}
ChangeCount(safeKey, -amount);
DailyTaskEventHub.ReportUseItem(amount);
return true;
}
public void SetCount(ExpBottleKind kind, int value)
{
var safeValue = Mathf.Max(0, value);
SetCountByKey(ExpBottleCatalog.GetKey(kind), safeValue);
}
public void ResetAllToZero()
{
InitializeIfNeeded();
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
countsByKey[ExpBottleCatalog.All[i].Key] = 0;
}
SaveNow();
NotifyAllCountsChanged();
}
public bool SeedFromPlayerSo(Player_SO playerData, bool overwriteExistingCounts)
{
if (playerData == null)
{
return false;
}
InitializeIfNeeded();
bool changed = false;
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
if (string.IsNullOrEmpty(descriptor.LegacyPlayerFieldName))
{
continue;
}
var legacyValue = Mathf.Max(0, playerData.GetLegacyExpBottleCount(descriptor.LegacyPlayerFieldName));
var currentValue = GetCountByKey(descriptor.Key);
if (!overwriteExistingCounts && currentValue > 0)
{
continue;
}
if (currentValue == legacyValue)
{
continue;
}
countsByKey[descriptor.Key] = legacyValue;
changed = true;
}
if (!changed)
{
return false;
}
SaveNow();
NotifyAllCountsChanged();
return true;
}
public bool HasEnough(ExpBottleKind kind, int amount)
{
if (amount <= 0)
{
return true;
}
return GetCount(kind) >= amount;
}
public bool HasEnoughByKey(string key, int amount)
{
if (amount <= 0)
{
return true;
}
if (string.IsNullOrWhiteSpace(key))
{
return false;
}
return GetCountByKey(key.Trim()) >= amount;
}
public void SaveNow()
{
InitializeIfNeededForSave();
SyncMirrorCounts();
ExpBottleLedgerStorage.TrySave(BuildPayload());
SyncToPlayerData();
}
private void SyncMirrorCounts()
{
expBottlesSO[] definitions = Resources.FindObjectsOfTypeAll<expBottlesSO>();
for (int i = 0; i < definitions.Length; i++)
{
expBottlesSO definition = definitions[i];
if (definition == null)
{
continue;
}
definition.runtimeOwnedCount = GetCount(definition.bottleKind);
}
}
private void InitializeIfNeededForSave()
{
if (!initialized)
{
InitializeIfNeeded();
}
}
private void ChangeCount(string key, int delta)
{
InitializeIfNeeded();
var current = GetCountByKey(key);
long next = (long)current + delta;
if (next < 0)
{
next = 0;
}
else if (next > int.MaxValue)
{
next = int.MaxValue;
}
SetCountByKey(key, (int)next);
}
private void SetCountByKey(string key, int value)
{
InitializeIfNeeded();
int safeValue = Mathf.Max(0, value);
int current = GetCountByKey(key);
if (current == safeValue)
{
return;
}
countsByKey[key] = safeValue;
SaveNow();
ExpBottleKind kind;
if (ExpBottleCatalog.TryGetKind(key, out kind) && OnBottleCountChanged != null)
{
OnBottleCountChanged(kind, safeValue);
}
}
private void RebuildFromPayload(ExpBottleLedgerPayload payload)
{
countsByKey.Clear();
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
countsByKey[ExpBottleCatalog.All[i].Key] = 0;
}
if (payload == null || payload.entries == null)
{
return;
}
for (int i = 0; i < payload.entries.Count; i++)
{
var entry = payload.entries[i];
if (entry == null || string.IsNullOrEmpty(entry.key))
{
continue;
}
countsByKey[entry.key] = Mathf.Max(0, entry.count);
}
}
private ExpBottleLedgerPayload BuildPayload()
{
var payload = ExpBottleLedgerStorage.CreateDefaultPayload();
payload.entries.Clear();
var serializedKeys = new HashSet<string>(StringComparer.Ordinal);
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
payload.entries.Add(new ExpBottleEntry
{
key = descriptor.Key,
count = GetCountByKey(descriptor.Key)
});
serializedKeys.Add(descriptor.Key);
}
foreach (var pair in countsByKey)
{
if (serializedKeys.Contains(pair.Key))
{
continue;
}
payload.entries.Add(new ExpBottleEntry
{
key = pair.Key,
count = Mathf.Max(0, pair.Value)
});
}
return payload;
}
private void NotifyAllCountsChanged()
{
if (OnBottleCountChanged == null)
{
return;
}
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
OnBottleCountChanged(descriptor.Kind, GetCountByKey(descriptor.Key));
}
}
private bool TryRecoverFromDefaultPlayerData()
{
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
if (player == null)
{
return false;
}
bool changed = false;
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
if (string.IsNullOrEmpty(descriptor.LegacyPlayerFieldName))
{
continue;
}
int legacyValue = Mathf.Max(0, player.GetLegacyExpBottleCount(descriptor.LegacyPlayerFieldName));
if (legacyValue <= 0)
{
continue;
}
if (GetCountByKey(descriptor.Key) > 0)
{
continue;
}
countsByKey[descriptor.Key] = legacyValue;
changed = true;
}
if (changed)
{
Debug.LogWarning("[ExpBottleLedger] Recovered bottle counts from Player_SO mirror fields.");
}
return changed;
}
private void SyncToPlayerData()
{
if (boundPlayerData == null)
{
return;
}
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
{
var descriptor = ExpBottleCatalog.All[i];
if (string.IsNullOrEmpty(descriptor.LegacyPlayerFieldName))
{
continue;
}
boundPlayerData.SetLegacyExpBottleCount(descriptor.LegacyPlayerFieldName, GetCountByKey(descriptor.Key));
}
}
}