养成基本完毕,新UI,修bug,装备初步,
This commit is contained in:
@@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@ using System.Collections.Generic;
|
||||
public class AllyHeroDeployEntry
|
||||
{
|
||||
public int heroId;
|
||||
public int currentExp;
|
||||
public int unlockedTierIndex;
|
||||
public bool autoBreakthroughEnabled;
|
||||
public int deployCount;
|
||||
public int finishCount;
|
||||
public int mvpCount;
|
||||
public long joinDateUtcTicks;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -17,6 +17,11 @@ public static class AllyHeroDeployLedgerStorage
|
||||
private static string BackupFilePath => Path.Combine(VaultDirectoryPath, BackupFileName);
|
||||
private static string TempFilePath => Path.Combine(VaultDirectoryPath, TempFileName);
|
||||
|
||||
public static bool HasExistingSaveFile()
|
||||
{
|
||||
return File.Exists(MainFilePath) || File.Exists(BackupFilePath);
|
||||
}
|
||||
|
||||
public static bool TryLoad(out AllyHeroDeployLedgerPayload payload)
|
||||
{
|
||||
payload = CreateDefaultPayload();
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public static class AllyHeroGrowthService
|
||||
{
|
||||
private const string RuntimeRulesPath = "so/system/hero_growth_rules";
|
||||
|
||||
public static bool TryUseBottleOnHero(expBottlesSO bottleDefinition, AllyHero_SO hero, int bottleCount, out string failureMessage, out int consumedBottleCount, out int totalGrantedExp)
|
||||
{
|
||||
failureMessage = null;
|
||||
consumedBottleCount = 0;
|
||||
totalGrantedExp = 0;
|
||||
|
||||
if (bottleDefinition == null)
|
||||
{
|
||||
failureMessage = "经验瓶定义缺失";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hero == null)
|
||||
{
|
||||
failureMessage = "目标偶像不存在";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bottleDefinition.bottleType != expBottlesSO.BottleType.Normal)
|
||||
{
|
||||
failureMessage = "该经验瓶不是单体经验瓶";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bottleCount <= 0)
|
||||
{
|
||||
failureMessage = "经验瓶数量无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
HeroGrowthTier unlockedTier = GetUnlockedTier(hero);
|
||||
if (unlockedTier >= HeroGrowthTier.S)
|
||||
{
|
||||
failureMessage = "S级偶像不可继续培养";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanBottleServeTier(bottleDefinition.serviceLevel, unlockedTier))
|
||||
{
|
||||
failureMessage = "该经验瓶不可用于当前偶像等级";
|
||||
return false;
|
||||
}
|
||||
|
||||
List<AllyHero_SO.AllyLevelInfo> sortedLevels = GetSortedLevels(hero);
|
||||
if (sortedLevels.Count < 2)
|
||||
{
|
||||
failureMessage = "偶像等级数据不完整";
|
||||
return false;
|
||||
}
|
||||
|
||||
int tierIndex = Mathf.Clamp((int)unlockedTier, 0, sortedLevels.Count - 1);
|
||||
int currentExp = AllyHeroDeployLedger.EnsureInstance().GetCurrentExp(hero.ally_heroID);
|
||||
int currentCap = GetCurrentTierCap(sortedLevels, tierIndex);
|
||||
if (currentExp >= currentCap)
|
||||
{
|
||||
failureMessage = "当前等级已满,请先突破";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExpBottleLedger.EnsureInstance().HasEnough(bottleDefinition.bottleKind, bottleCount))
|
||||
{
|
||||
failureMessage = "经验瓶库存不足";
|
||||
return false;
|
||||
}
|
||||
|
||||
int nextExp = currentExp;
|
||||
for (int i = 0; i < bottleCount; i++)
|
||||
{
|
||||
if (nextExp >= currentCap)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int appliedExp = ResolveBottleExpGain(bottleDefinition, nextExp, currentCap);
|
||||
if (appliedExp <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int before = nextExp;
|
||||
nextExp = Mathf.Min(currentCap, nextExp + appliedExp);
|
||||
consumedBottleCount++;
|
||||
totalGrantedExp += Mathf.Max(0, nextExp - before);
|
||||
}
|
||||
|
||||
if (consumedBottleCount <= 0)
|
||||
{
|
||||
failureMessage = "当前经验瓶对该偶像无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExpBottleLedger.EnsureInstance().TryConsume(bottleDefinition.bottleKind, consumedBottleCount))
|
||||
{
|
||||
consumedBottleCount = 0;
|
||||
totalGrantedExp = 0;
|
||||
failureMessage = "经验瓶库存不足";
|
||||
return false;
|
||||
}
|
||||
|
||||
AllyHeroDeployLedger.EnsureInstance().SetCurrentExp(hero, nextExp);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryUseRainAllBottle(expBottlesSO bottleDefinition, IList<AllyHero_SO> heroes, int bottleCount, out string failureMessage, out int consumedBottleCount, out int totalGrantedExp)
|
||||
{
|
||||
failureMessage = null;
|
||||
consumedBottleCount = 0;
|
||||
totalGrantedExp = 0;
|
||||
|
||||
if (bottleDefinition == null)
|
||||
{
|
||||
failureMessage = "经验瓶定义缺失";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bottleDefinition.bottleType != expBottlesSO.BottleType.RainAll)
|
||||
{
|
||||
failureMessage = "该经验瓶不是雨露均沾经验瓶";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (heroes == null || heroes.Count == 0)
|
||||
{
|
||||
failureMessage = "没有可作用的偶像";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bottleCount <= 0)
|
||||
{
|
||||
failureMessage = "经验瓶数量无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExpBottleLedger.EnsureInstance().HasEnough(bottleDefinition.bottleKind, bottleCount))
|
||||
{
|
||||
failureMessage = "经验瓶库存不足";
|
||||
return false;
|
||||
}
|
||||
|
||||
int targetCount = Mathf.Max(0, bottleDefinition.rainAllTargetCount);
|
||||
int expPerHero = Mathf.Max(0, bottleDefinition.rainAllGrantedExpPerHero);
|
||||
if (targetCount <= 0 || expPerHero <= 0)
|
||||
{
|
||||
failureMessage = "雨露均沾经验瓶定义不完整";
|
||||
return false;
|
||||
}
|
||||
|
||||
int remainingUses = bottleCount;
|
||||
while (remainingUses > 0)
|
||||
{
|
||||
List<AllyHero_SO> eligible = BuildEligibleRainAllTargets(heroes);
|
||||
if (eligible.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ShuffleInPlace(eligible);
|
||||
int applyCount = Mathf.Min(targetCount, eligible.Count);
|
||||
int grantedThisUse = 0;
|
||||
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
AllyHero_SO hero = eligible[i];
|
||||
List<AllyHero_SO.AllyLevelInfo> sortedLevels = GetSortedLevels(hero);
|
||||
if (sortedLevels.Count < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tierIndex = AllyHeroDeployLedger.EnsureInstance().GetUnlockedTierIndex(hero.ally_heroID);
|
||||
int currentExp = AllyHeroDeployLedger.EnsureInstance().GetCurrentExp(hero.ally_heroID);
|
||||
int currentCap = GetCurrentTierCap(sortedLevels, tierIndex);
|
||||
if (currentExp >= currentCap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int nextExp = Mathf.Min(currentCap, currentExp + expPerHero);
|
||||
grantedThisUse += Mathf.Max(0, nextExp - currentExp);
|
||||
AllyHeroDeployLedger.EnsureInstance().SetCurrentExp(hero, nextExp);
|
||||
}
|
||||
|
||||
consumedBottleCount++;
|
||||
totalGrantedExp += grantedThisUse;
|
||||
remainingUses--;
|
||||
}
|
||||
|
||||
if (consumedBottleCount <= 0)
|
||||
{
|
||||
failureMessage = "没有可被该经验瓶作用的偶像";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExpBottleLedger.EnsureInstance().TryConsume(bottleDefinition.bottleKind, consumedBottleCount))
|
||||
{
|
||||
consumedBottleCount = 0;
|
||||
totalGrantedExp = 0;
|
||||
failureMessage = "经验瓶库存不足";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryBreakthrough(AllyHero_SO hero, out string failureMessage)
|
||||
{
|
||||
failureMessage = null;
|
||||
|
||||
if (hero == null)
|
||||
{
|
||||
failureMessage = "目标偶像不存在";
|
||||
return false;
|
||||
}
|
||||
|
||||
HeroGrowthTier unlockedTier = GetUnlockedTier(hero);
|
||||
if (unlockedTier >= HeroGrowthTier.S)
|
||||
{
|
||||
failureMessage = "当前偶像已到最高培养等级";
|
||||
return false;
|
||||
}
|
||||
|
||||
List<AllyHero_SO.AllyLevelInfo> sortedLevels = GetSortedLevels(hero);
|
||||
if (sortedLevels.Count < 2)
|
||||
{
|
||||
failureMessage = "偶像等级数据不完整";
|
||||
return false;
|
||||
}
|
||||
|
||||
int tierIndex = Mathf.Clamp((int)unlockedTier, 0, sortedLevels.Count - 1);
|
||||
int currentExp = AllyHeroDeployLedger.EnsureInstance().GetCurrentExp(hero.ally_heroID);
|
||||
int currentCap = GetCurrentTierCap(sortedLevels, tierIndex);
|
||||
if (currentExp < currentCap)
|
||||
{
|
||||
failureMessage = "当前等级经验未满,无法突破";
|
||||
return false;
|
||||
}
|
||||
|
||||
HeroGrowthRulesSO rules = LoadRules();
|
||||
if (rules == null)
|
||||
{
|
||||
failureMessage = "未配置成长规则";
|
||||
return false;
|
||||
}
|
||||
|
||||
HeroGrowthRulesSO.BreakthroughRule rule = FindBreakthroughRule(rules, unlockedTier);
|
||||
if (rule == null || rule.requirements == null || rule.requirements.Count == 0)
|
||||
{
|
||||
failureMessage = "未配置突破材料";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rule.requirements.Count; i++)
|
||||
{
|
||||
HeroGrowthRulesSO.MaterialRequirement requirement = rule.requirements[i];
|
||||
if (requirement == null || requirement.amount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DushMaterialLedger.EnsureInstance().GetCount(requirement.materialKind) < requirement.amount)
|
||||
{
|
||||
failureMessage = "突破材料不足";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool anyPositiveRequirement = false;
|
||||
for (int i = 0; i < rule.requirements.Count; i++)
|
||||
{
|
||||
HeroGrowthRulesSO.MaterialRequirement requirement = rule.requirements[i];
|
||||
if (requirement == null || requirement.amount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
anyPositiveRequirement = true;
|
||||
if (!DushMaterialLedger.EnsureInstance().TryConsume(requirement.materialKind, requirement.amount))
|
||||
{
|
||||
failureMessage = "突破材料不足";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyPositiveRequirement)
|
||||
{
|
||||
failureMessage = "未配置突破材料";
|
||||
return false;
|
||||
}
|
||||
|
||||
AllyHeroDeployLedger.EnsureInstance().SetUnlockedTierIndex(hero, tierIndex + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static HeroGrowthTier GetUnlockedTier(AllyHero_SO hero)
|
||||
{
|
||||
if (hero == null)
|
||||
{
|
||||
return HeroGrowthTier.C;
|
||||
}
|
||||
|
||||
return (HeroGrowthTier)Mathf.Clamp(AllyHeroDeployLedger.EnsureInstance().GetUnlockedTierIndex(hero.ally_heroID), 0, 3);
|
||||
}
|
||||
|
||||
private static HeroGrowthRulesSO LoadRules()
|
||||
{
|
||||
return Resources.Load<HeroGrowthRulesSO>(RuntimeRulesPath);
|
||||
}
|
||||
|
||||
private static HeroGrowthRulesSO.BreakthroughRule FindBreakthroughRule(HeroGrowthRulesSO rules, HeroGrowthTier tier)
|
||||
{
|
||||
if (rules == null || rules.breakthroughRules == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rules.breakthroughRules.Count; i++)
|
||||
{
|
||||
HeroGrowthRulesSO.BreakthroughRule rule = rules.breakthroughRules[i];
|
||||
if (rule != null && rule.fromTier == tier)
|
||||
{
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool CanBottleServeTier(expBottlesSO.ServiceLevel serviceLevel, HeroGrowthTier tier)
|
||||
{
|
||||
switch (serviceLevel)
|
||||
{
|
||||
case expBottlesSO.ServiceLevel.Any:
|
||||
return true;
|
||||
case expBottlesSO.ServiceLevel.C:
|
||||
return tier == HeroGrowthTier.C;
|
||||
case expBottlesSO.ServiceLevel.B:
|
||||
return tier == HeroGrowthTier.B;
|
||||
case expBottlesSO.ServiceLevel.A:
|
||||
return tier == HeroGrowthTier.A;
|
||||
case expBottlesSO.ServiceLevel.S:
|
||||
return tier == HeroGrowthTier.S;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int ResolveBottleExpGain(expBottlesSO bottleDefinition, int currentExp, int currentCap)
|
||||
{
|
||||
switch (bottleDefinition.expApplyMode)
|
||||
{
|
||||
case expBottlesSO.ExpApplyMode.FillCurrentLevel:
|
||||
return Mathf.Max(0, currentCap - currentExp);
|
||||
case expBottlesSO.ExpApplyMode.FillAllLevels:
|
||||
return Mathf.Max(0, currentCap - currentExp);
|
||||
default:
|
||||
return Mathf.Max(0, bottleDefinition.grantedExp);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetCurrentTierCap(List<AllyHero_SO.AllyLevelInfo> sortedLevels, int tierIndex)
|
||||
{
|
||||
int nextIndex = Mathf.Clamp(tierIndex + 1, 0, sortedLevels.Count - 1);
|
||||
if (nextIndex == tierIndex)
|
||||
{
|
||||
return sortedLevels[tierIndex].requiredEXP;
|
||||
}
|
||||
|
||||
return Mathf.Max(sortedLevels[tierIndex].requiredEXP, sortedLevels[nextIndex].requiredEXP);
|
||||
}
|
||||
|
||||
private static List<AllyHero_SO.AllyLevelInfo> GetSortedLevels(AllyHero_SO hero)
|
||||
{
|
||||
List<AllyHero_SO.AllyLevelInfo> sorted = new List<AllyHero_SO.AllyLevelInfo>();
|
||||
if (hero == null || hero.levelStats == null)
|
||||
{
|
||||
return sorted;
|
||||
}
|
||||
|
||||
for (int i = 0; i < hero.levelStats.Count; i++)
|
||||
{
|
||||
if (hero.levelStats[i] != null)
|
||||
{
|
||||
sorted.Add(hero.levelStats[i]);
|
||||
}
|
||||
}
|
||||
|
||||
sorted.Sort((left, right) => left.requiredEXP.CompareTo(right.requiredEXP));
|
||||
return sorted;
|
||||
}
|
||||
|
||||
private static List<AllyHero_SO> BuildEligibleRainAllTargets(IList<AllyHero_SO> heroes)
|
||||
{
|
||||
List<AllyHero_SO> result = new List<AllyHero_SO>();
|
||||
for (int i = 0; i < heroes.Count; i++)
|
||||
{
|
||||
AllyHero_SO hero = heroes[i];
|
||||
if (hero == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HeroGrowthTier tier = GetUnlockedTier(hero);
|
||||
if (tier >= HeroGrowthTier.S)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
List<AllyHero_SO.AllyLevelInfo> sortedLevels = GetSortedLevels(hero);
|
||||
if (sortedLevels.Count < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentExp = AllyHeroDeployLedger.EnsureInstance().GetCurrentExp(hero.ally_heroID);
|
||||
int currentCap = GetCurrentTierCap(sortedLevels, (int)tier);
|
||||
if (currentExp >= currentCap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(hero);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ShuffleInPlace<T>(IList<T> list)
|
||||
{
|
||||
for (int i = list.Count - 1; i > 0; i--)
|
||||
{
|
||||
int swapIndex = Random.Range(0, i + 1);
|
||||
T temp = list[i];
|
||||
list[i] = list[swapIndex];
|
||||
list[swapIndex] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8da2914f05632cc46b1a6e4185597d99
|
||||
@@ -70,6 +70,7 @@ public sealed class DushMaterialLedger : MonoBehaviour
|
||||
loadedFromSave = DushMaterialLedgerStorage.TryLoad(out payload);
|
||||
RebuildFromPayload(payload);
|
||||
initialized = true;
|
||||
SyncMirrorCounts();
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
@@ -162,6 +163,19 @@ public sealed class DushMaterialLedger : MonoBehaviour
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetAllToZero()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
|
||||
for (int i = 0; i < DushMaterialCatalog.All.Count; i++)
|
||||
{
|
||||
countsByKey[DushMaterialCatalog.All[i].Key] = 0;
|
||||
}
|
||||
|
||||
SaveNow();
|
||||
NotifyAllCountsChanged();
|
||||
}
|
||||
|
||||
public void SaveNow()
|
||||
{
|
||||
if (!initialized)
|
||||
@@ -169,10 +183,26 @@ public sealed class DushMaterialLedger : MonoBehaviour
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
SyncMirrorCounts();
|
||||
DushMaterialLedgerStorage.TrySave(BuildPayload());
|
||||
SyncToPlayerData();
|
||||
}
|
||||
|
||||
private void SyncMirrorCounts()
|
||||
{
|
||||
growthMaterialSO[] definitions = Resources.FindObjectsOfTypeAll<growthMaterialSO>();
|
||||
for (int i = 0; i < definitions.Length; i++)
|
||||
{
|
||||
growthMaterialSO definition = definitions[i];
|
||||
if (definition == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
definition.runtimeOwnedCount = GetCount(definition.materialKind);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetCountByKey(string key)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
|
||||
@@ -77,6 +77,7 @@ public sealed class ExpBottleLedger : MonoBehaviour
|
||||
loadedFromSave = ExpBottleLedgerStorage.TryLoad(out payload);
|
||||
RebuildFromPayload(payload);
|
||||
initialized = true;
|
||||
SyncMirrorCounts();
|
||||
SaveNow();
|
||||
if (OnLedgerReloaded != null)
|
||||
{
|
||||
@@ -289,10 +290,26 @@ public sealed class ExpBottleLedger : MonoBehaviour
|
||||
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)
|
||||
|
||||
@@ -3,6 +3,13 @@ using UnityEngine;
|
||||
|
||||
public static class StoreExpBottlePurchaseService
|
||||
{
|
||||
private enum ConsumableGrantType
|
||||
{
|
||||
None,
|
||||
ExpBottle,
|
||||
GrowthMaterial
|
||||
}
|
||||
|
||||
public static bool TryPurchase(Player_SO playerData, storeItemSO itemSO, int packageCount, out string failureMessage, out int grantedCount)
|
||||
{
|
||||
failureMessage = string.Empty;
|
||||
@@ -127,10 +134,12 @@ public static class StoreExpBottlePurchaseService
|
||||
switch (itemSO.itemType)
|
||||
{
|
||||
case storeItemSO.ItemType.consumable:
|
||||
ConsumableGrantType grantType;
|
||||
ExpBottleKind bottleKind;
|
||||
if (!StoreExpBottleGrantResolver.TryResolve(itemSO, out bottleKind))
|
||||
DushMaterialKind materialKind;
|
||||
if (!TryResolveConsumableGrant(itemSO, out grantType, out bottleKind, out materialKind))
|
||||
{
|
||||
failureMessage = "当前仅支持经验瓶发放";
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -158,16 +167,31 @@ public static class StoreExpBottlePurchaseService
|
||||
switch (itemSO.itemType)
|
||||
{
|
||||
case storeItemSO.ItemType.consumable:
|
||||
ConsumableGrantType grantType;
|
||||
ExpBottleKind bottleKind;
|
||||
if (!StoreExpBottleGrantResolver.TryResolve(itemSO, out bottleKind))
|
||||
DushMaterialKind materialKind;
|
||||
if (!TryResolveConsumableGrant(itemSO, out grantType, out bottleKind, out materialKind))
|
||||
{
|
||||
failureMessage = "当前仅支持经验瓶发放";
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpBottleLedger.EnsureInstance().AttachPlayerData(playerData);
|
||||
ExpBottleLedger.EnsureInstance().Add(bottleKind, grantedCount);
|
||||
return true;
|
||||
if (grantType == ConsumableGrantType.ExpBottle)
|
||||
{
|
||||
ExpBottleLedger.EnsureInstance().AttachPlayerData(playerData);
|
||||
ExpBottleLedger.EnsureInstance().Add(bottleKind, grantedCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (grantType == ConsumableGrantType.GrowthMaterial)
|
||||
{
|
||||
DushMaterialLedger.EnsureInstance().AttachPlayerData(playerData);
|
||||
DushMaterialLedger.EnsureInstance().Add(materialKind, grantedCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
return false;
|
||||
|
||||
case storeItemSO.ItemType.character:
|
||||
case storeItemSO.ItemType.song:
|
||||
@@ -197,6 +221,25 @@ public static class StoreExpBottlePurchaseService
|
||||
return;
|
||||
}
|
||||
|
||||
ConsumableGrantType grantType;
|
||||
ExpBottleKind bottleKind;
|
||||
DushMaterialKind materialKind;
|
||||
if (!TryResolveConsumableGrant(itemSO, out grantType, out bottleKind, out materialKind))
|
||||
{
|
||||
Debug.Log(builder.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (grantType == ConsumableGrantType.GrowthMaterial)
|
||||
{
|
||||
builder.Append(" | 突破材料库存=");
|
||||
builder.Append(DushMaterialCatalog.GetDisplayName(materialKind));
|
||||
builder.Append(":");
|
||||
builder.Append(DushMaterialLedger.EnsureInstance().GetCount(materialKind));
|
||||
Debug.Log(builder.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
var snapshot = ExpBottleLedger.EnsureInstance().GetSnapshot();
|
||||
builder.Append(" | 经验瓶库存:");
|
||||
|
||||
@@ -223,4 +266,38 @@ public static class StoreExpBottlePurchaseService
|
||||
|
||||
Debug.Log(builder.ToString());
|
||||
}
|
||||
|
||||
private static bool TryResolveConsumableGrant(storeItemSO itemSO, out ConsumableGrantType grantType, out ExpBottleKind bottleKind, out DushMaterialKind materialKind)
|
||||
{
|
||||
grantType = ConsumableGrantType.None;
|
||||
bottleKind = default(ExpBottleKind);
|
||||
materialKind = default(DushMaterialKind);
|
||||
|
||||
if (itemSO == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemSO.associatedExpBottle != null)
|
||||
{
|
||||
grantType = ConsumableGrantType.ExpBottle;
|
||||
bottleKind = itemSO.associatedExpBottle.bottleKind;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemSO.associatedGrowthMaterial != null)
|
||||
{
|
||||
grantType = ConsumableGrantType.GrowthMaterial;
|
||||
materialKind = itemSO.associatedGrowthMaterial.materialKind;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StoreExpBottleGrantResolver.TryResolve(itemSO, out bottleKind))
|
||||
{
|
||||
grantType = ConsumableGrantType.ExpBottle;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +501,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
if (entry.owned && itemSO.associatedAllyHero != null)
|
||||
{
|
||||
itemSO.associatedAllyHero.isUnlocked = true;
|
||||
itemSO.associatedAllyHero.SetJoinDateIfMissing();
|
||||
MarkDirty(itemSO.associatedAllyHero);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user