443 lines
10 KiB
C#
443 lines
10 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;
|
|
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 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));
|
|
}
|
|
}
|
|
}
|