using System.Collections.Generic; using UnityEngine; public static class AllyHeroLevelRuntimeResolver { public static int GetCurrentExp(AllyHero_SO hero) { if (hero == null) { return 0; } if (hero.ally_heroID <= 0) { return Mathf.Max(0, hero.ally_currentEXP); } return AllyHeroDeployLedger.EnsureInstance().GetCurrentExp(hero.ally_heroID); } public static int GetUnlockedTierIndex(AllyHero_SO hero) { if (hero == null) { return 0; } if (hero.ally_heroID <= 0) { return Mathf.Clamp(hero.ally_growthUnlockedTierIndex, 0, 3); } return AllyHeroDeployLedger.EnsureInstance().GetUnlockedTierIndex(hero.ally_heroID); } public static string GetDisplayRatingKey(AllyHero_SO hero) { if (hero == null) { return "C"; } if (hero.ally_heroID <= 0) { return hero.GetDisplayLevelRatingKey(); } AllyHeroDeployLedger ledger = AllyHeroDeployLedger.EnsureInstance(); int currentExp = ledger.GetCurrentExp(hero.ally_heroID); int unlockedTier = ledger.GetUnlockedTierIndex(hero.ally_heroID); bool levelLocked = ledger.IsLevelLockEnabled(hero.ally_heroID); return hero.GetDisplayLevelRatingKeyFromGrowth(currentExp, unlockedTier, levelLocked); } public static AllyHero_SO.AllyLevelInfo GetBaseLevelInfo(AllyHero_SO hero) { if (hero == null) { return null; } List levels = GetSortedLevels(hero); if (levels == null || levels.Count == 0) { return null; } int currentExp = GetCurrentExp(hero); int expQualifiedIndex = 0; for (int i = 0; i < levels.Count; i++) { AllyHero_SO.AllyLevelInfo level = levels[i]; if (level != null && currentExp >= level.requiredEXP) { expQualifiedIndex = i; } else { break; } } int unlockedIndex = Mathf.Clamp(GetUnlockedTierIndex(hero), 0, levels.Count - 1); int effectiveIndex = Mathf.Clamp(Mathf.Min(expQualifiedIndex, unlockedIndex), 0, levels.Count - 1); return levels[effectiveIndex]; } public static AllyHero_SO.AllyLevelInfo GetEffectiveLevelInfo(AllyHero_SO hero) { AllyHero_SO.AllyLevelInfo baseInfo = GetBaseLevelInfo(hero); if (hero == null || baseInfo == null) { return null; } AllyHero_SO.AllyLevelInfo resolved = hero.GetEffectiveLevelInfoWithEquipment(baseInfo); if (resolved != null) { resolved.skill_slot_limited = GetEffectiveSkillSlotLimit(hero); } return resolved; } public static int GetCurrentLevelId(AllyHero_SO hero) { AllyHero_SO.AllyLevelInfo levelInfo = GetBaseLevelInfo(hero); return levelInfo != null ? levelInfo.levelID : 0; } public static int GetCurrentTierNumber(AllyHero_SO hero) { return Mathf.Clamp(GetUnlockedTierIndex(hero) + 1, 1, 4); } public static int GetEffectiveSkillSlotLimit(AllyHero_SO hero) { if (hero == null) { return 0; } AllyHero_SO.AllyLevelInfo baseInfo = GetBaseLevelInfo(hero); int baseSlotLimit = baseInfo != null ? Mathf.Max(0, baseInfo.skill_slot_limited) : 0; // Preserve equipment-only slot bonuses from the existing SO method, while replacing its base level source with the ledger. AllyHero_SO.AllyLevelInfo soLevelInfo = hero.GetEffectiveLevelForCurrentEXP(); int soBaseSlotLimit = soLevelInfo != null ? Mathf.Max(0, soLevelInfo.skill_slot_limited) : 0; int equipmentExtraSlots = Mathf.Max(0, hero.GetEffectiveSkillSlotLimit() - soBaseSlotLimit); return baseSlotLimit + equipmentExtraSlots; } private static List GetSortedLevels(AllyHero_SO hero) { if (hero == null || hero.levelStats == null || hero.levelStats.Count == 0) { return null; } List levels = new List(); for (int i = 0; i < hero.levelStats.Count; i++) { if (hero.levelStats[i] != null) { levels.Add(hero.levelStats[i]); } } if (levels.Count == 0) { return null; } levels.Sort((left, right) => left.requiredEXP.CompareTo(right.requiredEXP)); return levels; } }