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

445 lines
14 KiB
C#

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;
}
}
}