养成基本完毕,新UI,修bug,装备初步,
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user