养成基本完毕,新UI,修bug,装备初步,

This commit is contained in:
FloatGaming
2026-03-23 22:37:14 +08:00
parent 49e45ac464
commit dd205b6cb4
2349 changed files with 261722 additions and 283 deletions
@@ -8,9 +8,15 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
{
public static AllyHeroDeployLedger Instance { get; private set; }
public event System.Action<int> OnHeroGrowthChanged;
private readonly Dictionary<int, int> currentExpByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, int> unlockedTierByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, bool> autoBreakthroughEnabledByHeroId = new Dictionary<int, bool>();
private readonly Dictionary<int, int> deployCountsByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, int> finishCountsByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, int> mvpCountsByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, long> joinDateUtcTicksByHeroId = new Dictionary<int, long>();
private bool initialized;
private bool loadedFromSave;
@@ -24,6 +30,7 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
{
if (Instance != null)
{
Instance.ResetToDefaultsIfSaveMissing();
return Instance;
}
@@ -45,6 +52,7 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeIfNeeded();
ResetToDefaultsIfSaveMissing();
}
private void OnApplicationPause(bool pauseStatus)
@@ -71,6 +79,27 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
loadedFromSave = AllyHeroDeployLedgerStorage.TryLoad(out payload);
RebuildFromPayload(payload);
initialized = true;
if (!loadedFromSave)
{
ResetGrowthStateToDefaults();
}
SyncAllMirrorFlags();
SaveNow();
}
public void ResetToDefaultsIfSaveMissing()
{
if (!initialized)
{
return;
}
if (AllyHeroDeployLedgerStorage.HasExistingSaveFile())
{
return;
}
ResetGrowthStateToDefaults();
SyncAllMirrorFlags();
SaveNow();
}
@@ -96,6 +125,91 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
return mvpCountsByHeroId.TryGetValue(heroId, out count) ? count : 0;
}
public long GetJoinDateUtcTicks(int heroId)
{
InitializeIfNeeded();
long ticks;
return joinDateUtcTicksByHeroId.TryGetValue(heroId, out ticks) ? ticks : 0L;
}
public int GetCurrentExp(int heroId)
{
InitializeIfNeeded();
int count;
return currentExpByHeroId.TryGetValue(heroId, out count) ? Mathf.Max(0, count) : 0;
}
public int GetUnlockedTierIndex(int heroId)
{
InitializeIfNeeded();
int value;
return unlockedTierByHeroId.TryGetValue(heroId, out value) ? Mathf.Clamp(value, 0, 3) : 0;
}
public bool IsAutoBreakthroughEnabled(int heroId)
{
InitializeIfNeeded();
bool value;
return autoBreakthroughEnabledByHeroId.TryGetValue(heroId, out value) && value;
}
public void SetCurrentExp(AllyHero_SO hero, int value)
{
if (hero == null || hero.ally_heroID <= 0)
{
return;
}
InitializeIfNeeded();
int safeValue = Mathf.Max(0, value);
currentExpByHeroId[hero.ally_heroID] = safeValue;
hero.ally_currentEXP = safeValue;
MarkDirty(hero);
SaveNow();
if (OnHeroGrowthChanged != null)
{
OnHeroGrowthChanged(hero.ally_heroID);
}
}
public void SetUnlockedTierIndex(AllyHero_SO hero, int value)
{
if (hero == null || hero.ally_heroID <= 0)
{
return;
}
InitializeIfNeeded();
int safeValue = Mathf.Clamp(value, 0, 3);
unlockedTierByHeroId[hero.ally_heroID] = safeValue;
hero.ally_growthUnlockedTierIndex = safeValue;
hero.ally_currentEXP = GetCurrentExp(hero.ally_heroID);
MarkDirty(hero);
SaveNow();
if (OnHeroGrowthChanged != null)
{
OnHeroGrowthChanged(hero.ally_heroID);
}
}
public void SetAutoBreakthroughEnabled(AllyHero_SO hero, bool value)
{
if (hero == null || hero.ally_heroID <= 0)
{
return;
}
InitializeIfNeeded();
autoBreakthroughEnabledByHeroId[hero.ally_heroID] = value;
hero.ally_autoBreakthroughEnabled = value;
MarkDirty(hero);
SaveNow();
if (OnHeroGrowthChanged != null)
{
OnHeroGrowthChanged(hero.ally_heroID);
}
}
public void IncrementDeployCount(AllyHero_SO hero, int amount = 1)
{
if (hero == null || hero.ally_heroID <= 0 || amount <= 0)
@@ -144,6 +258,32 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
SaveNow();
}
public void SetJoinDateIfMissing(AllyHero_SO hero, System.DateTime? utcTime = null)
{
if (hero == null || hero.ally_heroID <= 0)
{
return;
}
InitializeIfNeeded();
if (GetJoinDateUtcTicks(hero.ally_heroID) > 0L)
{
hero.ally_joinDateUtcTicks = GetJoinDateUtcTicks(hero.ally_heroID);
return;
}
long ticks = (utcTime ?? System.DateTime.UtcNow).Ticks;
if (ticks <= 0L)
{
return;
}
joinDateUtcTicksByHeroId[hero.ally_heroID] = ticks;
hero.ally_joinDateUtcTicks = ticks;
MarkDirty(hero);
SaveNow();
}
public void SaveNow()
{
if (!initialized)
@@ -155,16 +295,34 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
AllyHeroDeployLedgerStorage.TrySave(BuildPayload());
}
public void ResetGrowthProgressOnly()
{
InitializeIfNeeded();
currentExpByHeroId.Clear();
unlockedTierByHeroId.Clear();
autoBreakthroughEnabledByHeroId.Clear();
ResetGrowthStateToDefaults();
ClearAllPendingDebtKeys();
SyncAllMirrorFlags();
SaveNow();
}
private void RebuildFromPayload(AllyHeroDeployLedgerPayload payload)
{
currentExpByHeroId.Clear();
unlockedTierByHeroId.Clear();
autoBreakthroughEnabledByHeroId.Clear();
deployCountsByHeroId.Clear();
finishCountsByHeroId.Clear();
mvpCountsByHeroId.Clear();
joinDateUtcTicksByHeroId.Clear();
if (payload == null || payload.entries == null)
{
return;
}
Dictionary<int, AllyHero_SO> heroById = BuildHeroLookup();
for (int i = 0; i < payload.entries.Count; i++)
{
AllyHeroDeployEntry entry = payload.entries[i];
@@ -173,9 +331,62 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
continue;
}
int currentExp = Mathf.Max(0, entry.currentExp);
int unlockedTier = Mathf.Clamp(entry.unlockedTierIndex, 0, 3);
AllyHero_SO hero;
if (heroById.TryGetValue(entry.heroId, out hero))
{
NormalizeGrowthState(hero, ref currentExp, ref unlockedTier);
}
currentExpByHeroId[entry.heroId] = currentExp;
unlockedTierByHeroId[entry.heroId] = unlockedTier;
autoBreakthroughEnabledByHeroId[entry.heroId] = entry.autoBreakthroughEnabled;
deployCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.deployCount);
finishCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.finishCount);
mvpCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.mvpCount);
if (entry.joinDateUtcTicks > 0L)
{
joinDateUtcTicksByHeroId[entry.heroId] = entry.joinDateUtcTicks;
}
}
}
private void ResetGrowthStateToDefaults()
{
currentExpByHeroId.Clear();
unlockedTierByHeroId.Clear();
autoBreakthroughEnabledByHeroId.Clear();
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0)
{
continue;
}
currentExpByHeroId[hero.ally_heroID] = 0;
unlockedTierByHeroId[hero.ally_heroID] = 0;
autoBreakthroughEnabledByHeroId[hero.ally_heroID] = false;
if (hero.ally_currentEXP != 0)
{
hero.ally_currentEXP = 0;
MarkDirty(hero);
}
if (hero.ally_growthUnlockedTierIndex != 0)
{
hero.ally_growthUnlockedTierIndex = 0;
MarkDirty(hero);
}
if (hero.ally_autoBreakthroughEnabled)
{
hero.ally_autoBreakthroughEnabled = false;
MarkDirty(hero);
}
}
}
@@ -196,6 +407,31 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
count = 0;
}
int currentExp;
if (!currentExpByHeroId.TryGetValue(hero.ally_heroID, out currentExp))
{
currentExp = 0;
currentExpByHeroId[hero.ally_heroID] = currentExp;
}
int unlockedTier;
if (!unlockedTierByHeroId.TryGetValue(hero.ally_heroID, out unlockedTier))
{
unlockedTier = 0;
unlockedTierByHeroId[hero.ally_heroID] = unlockedTier;
}
bool autoBreakthroughEnabled;
if (!autoBreakthroughEnabledByHeroId.TryGetValue(hero.ally_heroID, out autoBreakthroughEnabled))
{
autoBreakthroughEnabled = false;
autoBreakthroughEnabledByHeroId[hero.ally_heroID] = autoBreakthroughEnabled;
}
NormalizeGrowthState(hero, ref currentExp, ref unlockedTier);
currentExpByHeroId[hero.ally_heroID] = currentExp;
unlockedTierByHeroId[hero.ally_heroID] = unlockedTier;
int finishCount;
if (!finishCountsByHeroId.TryGetValue(hero.ally_heroID, out finishCount))
{
@@ -208,7 +444,31 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
mvpCount = 0;
}
long joinTicks;
if (!joinDateUtcTicksByHeroId.TryGetValue(hero.ally_heroID, out joinTicks))
{
joinTicks = 0L;
}
bool changed = false;
if (hero.ally_currentEXP != currentExp)
{
hero.ally_currentEXP = currentExp;
changed = true;
}
if (hero.ally_growthUnlockedTierIndex != unlockedTier)
{
hero.ally_growthUnlockedTierIndex = unlockedTier;
changed = true;
}
if (hero.ally_autoBreakthroughEnabled != autoBreakthroughEnabled)
{
hero.ally_autoBreakthroughEnabled = autoBreakthroughEnabled;
changed = true;
}
if (hero.ally_battleDeployCount != count)
{
hero.ally_battleDeployCount = count;
@@ -227,6 +487,12 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
changed = true;
}
if (hero.ally_joinDateUtcTicks != joinTicks)
{
hero.ally_joinDateUtcTicks = joinTicks;
changed = true;
}
if (changed)
{
MarkDirty(hero);
@@ -234,10 +500,106 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
}
}
private static Dictionary<int, AllyHero_SO> BuildHeroLookup()
{
Dictionary<int, AllyHero_SO> heroById = new Dictionary<int, AllyHero_SO>();
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0 || heroById.ContainsKey(hero.ally_heroID))
{
continue;
}
heroById.Add(hero.ally_heroID, hero);
}
return heroById;
}
private static void NormalizeGrowthState(AllyHero_SO hero, ref int currentExp, ref int unlockedTier)
{
currentExp = Mathf.Max(0, currentExp);
unlockedTier = Mathf.Clamp(unlockedTier, 0, 3);
if (hero == null || hero.levelStats == null || hero.levelStats.Count == 0)
{
if (currentExp <= 0)
{
unlockedTier = 0;
}
return;
}
List<AllyHero_SO.AllyLevelInfo> levels = new List<AllyHero_SO.AllyLevelInfo>();
for (int i = 0; i < hero.levelStats.Count; i++)
{
if (hero.levelStats[i] != null)
{
levels.Add(hero.levelStats[i]);
}
}
if (levels.Count == 0)
{
if (currentExp <= 0)
{
unlockedTier = 0;
}
return;
}
levels.Sort((left, right) => left.requiredEXP.CompareTo(right.requiredEXP));
int reachableTier = 0;
for (int i = 0; i < levels.Count; i++)
{
if (currentExp >= Mathf.Max(0, levels[i].requiredEXP))
{
reachableTier = i;
}
}
unlockedTier = Mathf.Min(unlockedTier, reachableTier);
}
private static void ClearAllPendingDebtKeys()
{
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0)
{
continue;
}
PlayerPrefs.DeleteKey("idol_upgrade_pending_debt_" + hero.ally_heroID);
}
PlayerPrefs.Save();
}
private AllyHeroDeployLedgerPayload BuildPayload()
{
AllyHeroDeployLedgerPayload payload = AllyHeroDeployLedgerStorage.CreateDefaultPayload();
HashSet<int> allHeroIds = new HashSet<int>();
foreach (KeyValuePair<int, int> pair in currentExpByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, int> pair in unlockedTierByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, bool> pair in autoBreakthroughEnabledByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, int> pair in deployCountsByHeroId)
{
allHeroIds.Add(pair.Key);
@@ -253,14 +615,23 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, long> pair in joinDateUtcTicksByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (int heroId in allHeroIds)
{
payload.entries.Add(new AllyHeroDeployEntry
{
heroId = heroId,
currentExp = Mathf.Max(0, GetCurrentExp(heroId)),
unlockedTierIndex = Mathf.Clamp(GetUnlockedTierIndex(heroId), 0, 3),
autoBreakthroughEnabled = IsAutoBreakthroughEnabled(heroId),
deployCount = Mathf.Max(0, GetDeployCount(heroId)),
finishCount = Mathf.Max(0, GetFinishCount(heroId)),
mvpCount = Mathf.Max(0, GetMvpCount(heroId))
mvpCount = Mathf.Max(0, GetMvpCount(heroId)),
joinDateUtcTicks = GetJoinDateUtcTicks(heroId)
});
}