很多更新,服务端连接,新UI和系统

This commit is contained in:
FloatGaming
2026-04-17 20:09:41 +08:00
parent 85dfff28dd
commit e0bc0bbf08
910 changed files with 65191 additions and 1291 deletions
+5
View File
@@ -1676,6 +1676,10 @@ public class AllyCombatant : MonoBehaviour, ICombatant
int oldHP = currentHP;
float effective = amount * (1f - damageResistance);
int delta = Mathf.CeilToInt(effective);
if (SkillBuilder.Instance != null && SkillBuilder.Instance.TryPreventLethalDamage(this, delta))
{
return;
}
ModifyHP(-delta, true, true);
int actual = oldHP - currentHP;
@@ -1793,6 +1797,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
if (buff.scoreMultiplier != 1f) scoreEfficiency *= buff.scoreMultiplier;
if (buff.attackMultiplier != 1f) RecalculateAttackFromBuffs();
iBudeffPrefabController.Instance?.NotifyBuffApplied(this, buff);
SkillBuilder.Instance?.NotifyBuffApplied(slotIndex);
}
public void RemoveBuff(string buffId)
+812 -1
View File
@@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -77,6 +78,25 @@ public class SkillBuilder : MonoBehaviour
private readonly Dictionary<int, int> _equipManaFullSpendCountBySlot = new Dictionary<int, int>(8);
private readonly HashSet<int> _equipGhoulSchoolActiveSlots = new HashSet<int>();
private readonly Dictionary<int, HashSet<string>> _equipGhoulSchoolSeenSkillIdsBySlot = new Dictionary<int, HashSet<string>>(8);
private readonly HashSet<int> _equipThousandthActiveSlots = new HashSet<int>();
private readonly Dictionary<int, float> _equipThousandthAppliedBonusBySlot = new Dictionary<int, float>(8);
private readonly HashSet<int> _equipForgetfulRhythmActiveSlots = new HashSet<int>();
private readonly Dictionary<int, HashSet<string>> _equipForgetfulRhythmSeenSkillIdsBySlot = new Dictionary<int, HashSet<string>>(8);
private readonly Dictionary<int, int> _equipForgetfulRhythmStacksBySlot = new Dictionary<int, int>(8);
private readonly Dictionary<int, string> _equipTalentScoutLastBorrowedSkillBySlot = new Dictionary<int, string>(8);
private readonly Dictionary<int, Coroutine> _equipTalentScoutDisagreeCoroutinesBySlot = new Dictionary<int, Coroutine>(8);
private readonly Dictionary<int, float> _equipTalentScoutDisagreeRestoreScoreBySlot = new Dictionary<int, float>(8);
private readonly Dictionary<int, int> _equipOutOfLineKillCountsBySlot = new Dictionary<int, int>(8);
private readonly Dictionary<int, int> _equipStormStacksBySlot = new Dictionary<int, int>(8);
private readonly HashSet<int> _equipStormProcessingSlots = new HashSet<int>();
private readonly HashSet<int> _equipFateRelianceActiveSlots = new HashSet<int>();
private readonly Dictionary<int, int> _equipNewIdeaStacksBySlot = new Dictionary<int, int>(8);
private readonly Dictionary<int, List<Coroutine>> _equipNewIdeaExpireCoroutinesBySlot = new Dictionary<int, List<Coroutine>>(8);
private readonly HashSet<int> _equipSocialMaskActiveSlots = new HashSet<int>();
private readonly Dictionary<int, int> _equipSocialMaskOriginalMaxHpBySlot = new Dictionary<int, int>(8);
private readonly Dictionary<int, float> _equipSocialMaskOriginalScoreBySlot = new Dictionary<int, float>(8);
private readonly HashSet<int> _equipFutureActiveSlots = new HashSet<int>();
private bool _equipQuietTurnBroadcastInProgress;
private int _cachedAlliesFrame = -1;
private readonly List<GameObject> _cachedAllies = new List<GameObject>(8);
@@ -94,6 +114,32 @@ public class SkillBuilder : MonoBehaviour
private const string EquipSkillStartSetMana = "420010_start_set_mana";
private const string EquipSkillKillRefillMana = "420011_kill_refill_mana";
private const string EquipSkillManaSpendReduceCap = "420012_mana_spend_reduce_cap";
private const string EquipSkillVacantPerfectMana = "420013_vacant_perfect_mana";
private const string EquipSkillVacantMissHeal = "420014_vacant_miss_heal";
private const string EquipSkillThousandthTracker = "420015_thousandth_tracker";
private const string EquipSkillForgetfulRhythm = "420016_forgetful_rhythm";
private const string EquipSkillTalentScout = "420017_talent_scout";
private const string EquipSkillOutOfLineManaScore = "420018_out_of_line_mana_score";
private const string EquipSkillOutOfLineEnemyDead = "420019_out_of_line_enemy_dead";
private const string EquipSkillStormStart = "420020_storm_start";
private const string EquipSkillMissionStart = "420021_mission_start";
private const string EquipSkillMissionFullMana = "420022_mission_full_mana";
private const string EquipSkillAllForYouMana = "420023_all_for_you_mana";
private const string EquipSkillAllForYouKill = "420024_all_for_you_kill";
private const string EquipSkillFateRelianceStart = "420025_fate_reliance_start";
private const string EquipSkillNewIdeaKill = "420026_new_idea_kill";
private const string EquipSkillNewIdeaClear = "420027_new_idea_clear";
private const string EquipSkillNamelessLaneKill = "420028_nameless_lane_kill";
private const string EquipSkillSocialMaskStart = "420029_social_mask_start";
private const string EquipSkillSocialMaskKill = "420030_social_mask_kill";
private const string EquipSkillSocialMaskClear = "420031_social_mask_clear";
private const string EquipSkillQuietTurn = "420032_quiet_turn";
private const string EquipSkillBlock = "420033_block";
private const string EquipSkillFutureStart = "420034_future_start";
private const string EquipSkillFutureHeal = "420035_future_heal";
private const string EquipSkillFutureClear = "420036_future_clear";
private const string EquipSkillQuietTurnHealAdjacent = "420037_quiet_turn_heal_adjacent";
private const string EquipSkillBlockStart = "420038_block_start";
private const string IllusionSkillBole14StartMaxHp = "421001_bole14_start_maxhp";
private const string IllusionSkillBole6SpendFullMaxHp = "421002_bole6_spend_full_maxhp";
private const string IllusionSkillScarborough28StartAttack = "421003_scarborough28_start_attack";
@@ -1385,6 +1431,7 @@ ResolvedGroup:
}
NotifyGhoulSchoolSkillTriggered(def, slotIndex, caster);
NotifyForgetfulRhythmSkillTriggered(def, slotIndex);
if (TryHandleEquipmentSkill(def, slotIndex, caster, heroSoForName, specificTarget))
{
@@ -2012,6 +2059,58 @@ ResolvedGroup:
return HandleEquipSkillKillRefillMana(casterAlly);
case EquipSkillManaSpendReduceCap:
return HandleEquipSkillManaSpendReduceCap(casterAlly);
case EquipSkillVacantPerfectMana:
return HandleEquipSkillVacantPerfectMana(casterAlly);
case EquipSkillVacantMissHeal:
return HandleEquipSkillVacantMissHeal(casterAlly);
case EquipSkillThousandthTracker:
return HandleEquipSkillThousandthTracker(slotIndex);
case EquipSkillForgetfulRhythm:
return HandleEquipSkillForgetfulRhythm(slotIndex);
case EquipSkillTalentScout:
return HandleEquipSkillTalentScout(slotIndex);
case EquipSkillOutOfLineManaScore:
return HandleEquipSkillOutOfLineManaScore(slotIndex, casterAlly);
case EquipSkillOutOfLineEnemyDead:
return HandleEquipSkillOutOfLineEnemyDead(slotIndex);
case EquipSkillStormStart:
return HandleEquipSkillStormStart(slotIndex, casterAlly);
case EquipSkillMissionStart:
return HandleEquipSkillMissionStart(casterAlly);
case EquipSkillMissionFullMana:
return HandleEquipSkillMissionFullMana(casterAlly);
case EquipSkillAllForYouMana:
return HandleEquipSkillAllForYouMana(casterAlly);
case EquipSkillAllForYouKill:
return HandleEquipSkillAllForYouKill(casterAlly);
case EquipSkillFateRelianceStart:
return HandleEquipSkillFateRelianceStart(slotIndex);
case EquipSkillNewIdeaKill:
return HandleEquipSkillNewIdeaStack(slotIndex, 3f);
case EquipSkillNewIdeaClear:
return HandleEquipSkillNewIdeaStack(slotIndex, 8f);
case EquipSkillNamelessLaneKill:
return HandleEquipSkillNamelessLaneKill(slotIndex);
case EquipSkillSocialMaskStart:
return HandleEquipSkillSocialMaskStart(slotIndex, casterAlly);
case EquipSkillSocialMaskKill:
return HandleEquipSkillSocialMaskKill(slotIndex, casterAlly);
case EquipSkillSocialMaskClear:
return HandleEquipSkillSocialMaskClear(slotIndex, casterAlly);
case EquipSkillQuietTurn:
return HandleEquipSkillQuietTurn(casterAlly);
case EquipSkillBlock:
return HandleEquipSkillBlock(casterAlly);
case EquipSkillFutureStart:
return HandleEquipSkillFutureStart(slotIndex);
case EquipSkillFutureHeal:
return HandleEquipSkillFutureHeal(slotIndex, casterAlly);
case EquipSkillFutureClear:
return HandleEquipSkillFutureClear(slotIndex, casterAlly);
case EquipSkillQuietTurnHealAdjacent:
return HandleEquipSkillQuietTurnHealAdjacent(casterAlly);
case EquipSkillBlockStart:
return HandleEquipSkillBlockStart(casterAlly);
case IllusionSkillBole14StartMaxHp:
return HandleIllusionSkillBole14StartMaxHp(casterAlly);
case IllusionSkillBole6SpendFullMaxHp:
@@ -2066,6 +2165,185 @@ ResolvedGroup:
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_up, 0.01f, 0f);
}
private void NotifyForgetfulRhythmSkillTriggered(SkillDefinition def, int slotIndex)
{
if (def == null || slotIndex < 0) return;
if (def.skillId == EquipSkillForgetfulRhythm) return;
if (!_equipForgetfulRhythmActiveSlots.Contains(slotIndex)) return;
if (string.IsNullOrWhiteSpace(def.skillId)) return;
if (!_equipForgetfulRhythmSeenSkillIdsBySlot.TryGetValue(slotIndex, out HashSet<string> seen))
{
seen = new HashSet<string>();
_equipForgetfulRhythmSeenSkillIdsBySlot[slotIndex] = seen;
}
if (!seen.Add(def.skillId))
{
return;
}
int stacks = 0;
_equipForgetfulRhythmStacksBySlot.TryGetValue(slotIndex, out stacks);
_equipForgetfulRhythmStacksBySlot[slotIndex] = stacks + 1;
}
private void RefreshEquipThousandthBonus(int slotIndex)
{
if (!_equipThousandthActiveSlots.Contains(slotIndex)) return;
var allyGo = GetAllyObjectBySlot(slotIndex);
var ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally == null || ally.IsDead) return;
int currentCombo = teamUIController.Instance != null ? teamUIController.Instance.CurrentCombo : 0;
int perfectCount = ScoreManager.Instance != null ? Mathf.Max(0, ScoreManager.Instance.countPerfect) : 0;
float newBonus = 0f;
if (currentCombo >= 300)
{
newBonus += 0.02f;
}
newBonus += Mathf.FloorToInt(perfectCount / 100f) * 0.005f;
float previousBonus = 0f;
_equipThousandthAppliedBonusBySlot.TryGetValue(slotIndex, out previousBonus);
float delta = newBonus - previousBonus;
if (Mathf.Abs(delta) > 0.0001f)
{
ally.scoreEfficiency += delta;
_equipThousandthAppliedBonusBySlot[slotIndex] = newBonus;
}
}
private void ApplyForgetfulRhythmPerfectBonus(int slotIndex)
{
if (!_equipForgetfulRhythmActiveSlots.Contains(slotIndex)) return;
int stacks = 0;
_equipForgetfulRhythmStacksBySlot.TryGetValue(slotIndex, out stacks);
if (stacks <= 0) return;
ScoreManager scoreManager = ScoreManager.Instance;
if (scoreManager != null)
{
scoreManager.countPerfect += stacks;
if (slotIndex >= 0 && slotIndex < scoreManager.trackPerfectCounts.Length)
{
scoreManager.trackPerfectCounts[slotIndex] += stacks;
}
}
if (teamUIController.Instance != null)
{
for (int i = 0; i < stacks; i++)
{
teamUIController.Instance.OnJudgeResult("Perfect");
}
}
}
private void HandleEquipNoteJudgePassives(int slotIndex, string judgeResult)
{
if (slotIndex < 0 || string.IsNullOrWhiteSpace(judgeResult)) return;
if (judgeResult == "Perfect")
{
ApplyForgetfulRhythmPerfectBonus(slotIndex);
}
RefreshEquipThousandthBonus(slotIndex);
}
public void NotifyAllyAttackDealt(int slotIndex)
{
if (slotIndex < 0) return;
if (!_equipStormStacksBySlot.TryGetValue(slotIndex, out int stacks) || stacks <= 0) return;
if (_equipStormProcessingSlots.Contains(slotIndex)) return;
GameObject allyGo = GetAllyObjectBySlot(slotIndex);
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally == null || ally.IsDead || ally.attack <= 0) return;
if (EffectSystem.Instance == null) return;
float perHitDamage = Mathf.Max(1f, ally.attack / (float)stacks);
_equipStormProcessingSlots.Add(slotIndex);
try
{
for (int i = 0; i < stacks; i++)
{
EffectSystem.Instance.ApplyEffect(Selector.CurrentEnemies, EffectType.DamageSingleEnemy, perHitDamage, 0f, ally.gameObject, null);
}
}
finally
{
_equipStormProcessingSlots.Remove(slotIndex);
}
}
public void NotifyBuffApplied(int receiverSlotIndex)
{
if (receiverSlotIndex < 0) return;
GameObject receiverGo = GetAllyObjectBySlot(receiverSlotIndex);
AllyCombatant receiver = receiverGo != null ? receiverGo.GetComponent<AllyCombatant>() : null;
if (receiver == null || receiver.IsDead) return;
if (_equipFateRelianceActiveSlots.Contains(receiverSlotIndex))
{
int selfGain = Mathf.CeilToInt(receiver.maxMana * 0.10f);
if (selfGain > 0)
{
receiver.ModifyMana(selfGain, true, true);
}
}
if (teamUIController.Instance == null) return;
int[] adjacent = teamUIController.Instance.GetAdjacentAllyIndices(receiverSlotIndex);
if (adjacent == null) return;
for (int i = 0; i < adjacent.Length; i++)
{
int slot = adjacent[i];
if (!_equipFateRelianceActiveSlots.Contains(slot)) continue;
GameObject allyGo = GetAllyObjectBySlot(slot);
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally == null || ally.IsDead) continue;
int gain = Mathf.CeilToInt(ally.maxMana * 0.15f);
if (gain > 0)
{
ally.ModifyMana(gain, true, true);
}
}
}
public bool TryPreventLethalDamage(AllyCombatant ally, int incomingDamage)
{
if (ally == null || ally.IsDead) return false;
if (incomingDamage <= 0) return false;
if (ally.currentHP - incomingDamage > 0) return false;
int slotIndex = ally.slotIndex;
if (_equipSocialMaskActiveSlots.Contains(slotIndex))
{
DeactivateSocialMask(slotIndex, ally);
return true;
}
if (_equipFutureActiveSlots.Contains(slotIndex))
{
_equipFutureActiveSlots.Remove(slotIndex);
int restoreHp = Mathf.CeilToInt(ally.maxHP * 0.30f);
ally.SetCurrentHP(Mathf.Max(1, restoreHp), true);
return true;
}
return false;
}
private bool HandleEquipSkillLostMaster(AllyCombatant ally)
{
int overflow = Mathf.Max(0, ally.lastHealOverflowAmount);
@@ -2241,6 +2519,489 @@ ResolvedGroup:
return true;
}
private bool HandleEquipSkillVacantPerfectMana(AllyCombatant ally)
{
ally.ModifyMana(1, true, true);
return true;
}
private bool HandleEquipSkillVacantMissHeal(AllyCombatant ally)
{
int healAmount = Mathf.Max(0, ally.attack);
if (healAmount > 0)
{
ally.ModifyHP(healAmount, true);
}
return true;
}
private bool HandleEquipSkillThousandthTracker(int slotIndex)
{
_equipThousandthActiveSlots.Add(slotIndex);
_equipThousandthAppliedBonusBySlot[slotIndex] = 0f;
return true;
}
private bool HandleEquipSkillForgetfulRhythm(int slotIndex)
{
_equipForgetfulRhythmActiveSlots.Add(slotIndex);
_equipForgetfulRhythmStacksBySlot[slotIndex] = 0;
if (!_equipForgetfulRhythmSeenSkillIdsBySlot.ContainsKey(slotIndex))
{
_equipForgetfulRhythmSeenSkillIdsBySlot[slotIndex] = new HashSet<string>();
}
return true;
}
private bool HandleEquipSkillTalentScout(int slotIndex)
{
AllyHero_SO hero = GetAllyHeroSOBySlot(slotIndex);
if (hero == null) return true;
List<SkillDefinition> candidates = new List<SkillDefinition>();
HashSet<string> equippedIds = new HashSet<string>();
int[] equippedGroups = hero.GetEffectiveEquippedSkillGroupIDs();
if (equippedGroups != null)
{
for (int i = 0; i < equippedGroups.Length; i++)
{
SkillGroup equippedGroup = hero.GetSkillGroupByID(equippedGroups[i]);
if (equippedGroup == null || equippedGroup.skills == null) continue;
for (int j = 0; j < equippedGroup.skills.Length; j++)
{
SkillDefinition equippedDef = equippedGroup.skills[j];
if (equippedDef != null && !string.IsNullOrWhiteSpace(equippedDef.skillId))
{
equippedIds.Add(equippedDef.skillId);
}
}
}
}
if (hero.availableSkills != null)
{
for (int i = 0; i < hero.availableSkills.Length; i++)
{
SkillDefinition def = hero.availableSkills[i];
if (def == null || string.IsNullOrWhiteSpace(def.skillId)) continue;
if (equippedIds.Contains(def.skillId)) continue;
candidates.Add(def);
}
}
if (hero.skillGroups != null)
{
for (int i = 0; i < hero.skillGroups.Length; i++)
{
SkillGroup group = hero.skillGroups[i];
if (group == null || group.skills == null) continue;
bool groupEquipped = false;
if (equippedGroups != null)
{
for (int j = 0; j < equippedGroups.Length; j++)
{
if (equippedGroups[j] == group.skillGroupID)
{
groupEquipped = true;
break;
}
}
}
if (groupEquipped) continue;
for (int j = 0; j < group.skills.Length; j++)
{
SkillDefinition def = group.skills[j];
if (def == null || string.IsNullOrWhiteSpace(def.skillId)) continue;
if (equippedIds.Contains(def.skillId)) continue;
candidates.Add(def);
}
}
}
if (candidates.Count <= 0) return true;
SkillDefinition chosen = candidates[UnityEngine.Random.Range(0, candidates.Count)];
if (chosen == null) return true;
string heroObsession = hero != null ? (hero.obsessionTag ?? string.Empty).Trim() : string.Empty;
string chosenObsession = chosen != null ? (chosen.obsessionTag ?? string.Empty).Trim() : string.Empty;
bool obsessionMismatch =
!string.IsNullOrEmpty(heroObsession) &&
!string.IsNullOrEmpty(chosenObsession) &&
!string.Equals(heroObsession, chosenObsession, StringComparison.Ordinal);
if (obsessionMismatch)
{
ApplyTalentScoutDisagree(slotIndex);
}
_equipTalentScoutLastBorrowedSkillBySlot[slotIndex] = chosen.skillId;
UseSkillDefinition(chosen, slotIndex, -1f, null);
return true;
}
private void ApplyTalentScoutDisagree(int slotIndex)
{
GameObject allyGo = GetAllyObjectBySlot(slotIndex);
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally == null || ally.IsDead) return;
if (_equipTalentScoutDisagreeCoroutinesBySlot.TryGetValue(slotIndex, out Coroutine existing) && existing != null)
{
StopCoroutine(existing);
_equipTalentScoutDisagreeCoroutinesBySlot.Remove(slotIndex);
if (_equipTalentScoutDisagreeRestoreScoreBySlot.TryGetValue(slotIndex, out float oldScore))
{
ally.scoreEfficiency = Mathf.Max(0f, oldScore);
}
}
_equipTalentScoutDisagreeRestoreScoreBySlot[slotIndex] = ally.scoreEfficiency;
ally.scoreEfficiency = 0f;
_equipTalentScoutDisagreeCoroutinesBySlot[slotIndex] = StartCoroutine(TalentScoutDisagreeCoroutine(slotIndex, ally, 2f));
}
private IEnumerator TalentScoutDisagreeCoroutine(int slotIndex, AllyCombatant ally, float duration)
{
if (ally != null)
{
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_down, -ally.scoreEfficiency, duration);
}
yield return new WaitForSeconds(duration);
if (ally != null && !ally.IsDead && _equipTalentScoutDisagreeRestoreScoreBySlot.TryGetValue(slotIndex, out float restore))
{
ally.scoreEfficiency = Mathf.Max(0f, restore);
}
_equipTalentScoutDisagreeCoroutinesBySlot.Remove(slotIndex);
_equipTalentScoutDisagreeRestoreScoreBySlot.Remove(slotIndex);
}
private bool HandleEquipSkillOutOfLineManaScore(int slotIndex, AllyCombatant ally)
{
if (ally.lastManaSpentAmount <= 0) return true;
int killCount = 0;
_equipOutOfLineKillCountsBySlot.TryGetValue(slotIndex, out killCount);
ally.AddScoreDirect(75 + killCount * 55);
return true;
}
private bool HandleEquipSkillOutOfLineEnemyDead(int slotIndex)
{
int current = 0;
_equipOutOfLineKillCountsBySlot.TryGetValue(slotIndex, out current);
_equipOutOfLineKillCountsBySlot[slotIndex] = current + 1;
SwapEquippedMemoryWithLowerAdjacent(slotIndex);
return true;
}
private void SwapEquippedMemoryWithLowerAdjacent(int slotIndex)
{
int lowerSlot = (slotIndex + 1) % 5;
if (lowerSlot < 0 || lowerSlot >= 5 || lowerSlot == slotIndex) return;
AllyHero_SO self = GetAllyHeroSOBySlot(slotIndex);
AllyHero_SO lower = GetAllyHeroSOBySlot(lowerSlot);
if (self == null || lower == null) return;
equipmentSO selfEquip = self.GetEquippedEquipmentResolved();
equipmentSO lowerEquip = lower.GetEquippedEquipmentResolved();
self.SetEquippedEquipment(lowerEquip, false);
lower.SetEquippedEquipment(selfEquip, false);
}
private bool HandleEquipSkillStormStart(int slotIndex, AllyCombatant ally)
{
int reduce = Mathf.FloorToInt(ally.attack * 0.46f);
if (reduce > 0)
{
ally.ModifyAttack(-reduce);
}
_equipStormStacksBySlot[slotIndex] = 1;
return true;
}
private bool HandleEquipSkillMissionStart(AllyCombatant ally)
{
int bonusAttack = Mathf.FloorToInt(ally.damageResistance / 0.01f);
if (bonusAttack > 0)
{
ally.ModifyAttack(bonusAttack);
}
return true;
}
private bool HandleEquipSkillMissionFullMana(AllyCombatant ally)
{
if (!ally.lastManaSpendWasFullBar || ally.lastManaSpentAmount <= 0) return true;
ally.damageResistance = Mathf.Clamp01(ally.damageResistance + 0.04f);
return true;
}
private bool HandleEquipSkillAllForYouMana(AllyCombatant ally)
{
if (!ally.lastManaSpendWasFullBar || ally.lastManaSpentAmount <= 0) return true;
ally.ActivateSelfDamageRedirectToAdjacent(999f);
return true;
}
private bool HandleEquipSkillAllForYouKill(AllyCombatant ally)
{
ally.ActivateSelfDamageRedirectToAdjacent(999f);
return true;
}
private bool HandleEquipSkillFateRelianceStart(int slotIndex)
{
_equipFateRelianceActiveSlots.Add(slotIndex);
return true;
}
private bool HandleEquipSkillNewIdeaStack(int slotIndex, float duration)
{
int stacks = 0;
_equipNewIdeaStacksBySlot.TryGetValue(slotIndex, out stacks);
_equipNewIdeaStacksBySlot[slotIndex] = stacks + 1;
if (!_equipNewIdeaExpireCoroutinesBySlot.TryGetValue(slotIndex, out List<Coroutine> list) || list == null)
{
list = new List<Coroutine>();
_equipNewIdeaExpireCoroutinesBySlot[slotIndex] = list;
}
Coroutine expire = StartCoroutine(NewIdeaExpireCoroutine(slotIndex, duration));
list.Add(expire);
ApplyNewIdeaState(slotIndex);
return true;
}
private IEnumerator NewIdeaExpireCoroutine(int slotIndex, float duration)
{
yield return new WaitForSeconds(duration);
if (_equipNewIdeaStacksBySlot.TryGetValue(slotIndex, out int stacks))
{
_equipNewIdeaStacksBySlot[slotIndex] = Mathf.Max(0, stacks - 1);
}
ApplyNewIdeaState(slotIndex);
}
private void ApplyNewIdeaState(int slotIndex)
{
GameObject allyGo = GetAllyObjectBySlot(slotIndex);
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
AllyHero_SO hero = GetAllyHeroSOBySlot(slotIndex);
if (ally == null || hero == null || ally.IsDead) return;
int stacks = 0;
_equipNewIdeaStacksBySlot.TryGetValue(slotIndex, out stacks);
List<AllyHero_SO.AllyLevelInfo> sortedLevels = new List<AllyHero_SO.AllyLevelInfo>();
if (hero.levelStats != null)
{
for (int i = 0; i < hero.levelStats.Count; i++)
{
if (hero.levelStats[i] != null) sortedLevels.Add(hero.levelStats[i]);
}
}
if (sortedLevels.Count <= 0) return;
sortedLevels.Sort((left, right) => left.requiredEXP.CompareTo(right.requiredEXP));
int baseTierIndex = Mathf.Clamp((int)AllyHeroGrowthService.GetUnlockedTier(hero), 0, sortedLevels.Count - 1);
int targetTierIndex = Mathf.Clamp(baseTierIndex + stacks, 0, Mathf.Min(sortedLevels.Count - 1, 3));
AllyHero_SO.AllyLevelInfo targetInfo = hero.GetEffectiveLevelInfoWithEquipment(sortedLevels[targetTierIndex]);
if (targetInfo == null) return;
int currentHp = ally.currentHP;
int currentMana = ally.currentMana;
ally.SetMaxHP(Mathf.Max(1, targetInfo.maxHP), false);
ally.SetMaxMana(Mathf.Max(1, targetInfo.maxMana), false);
ally.SetAttack(Mathf.Max(0, targetInfo.attack));
ally.scoreEfficiency = Mathf.Max(0f, targetInfo.scoreEfficiency);
ally.damageResistance = Mathf.Clamp01(targetInfo.damageResistance);
ally.SetCurrentHP(Mathf.Clamp(currentHp, 0, ally.maxHP), false);
ally.SetCurrentMana(Mathf.Clamp(currentMana, 0, ally.maxMana), false, false);
}
private bool HandleEquipSkillNamelessLaneKill(int slotIndex)
{
GameObject allyGo = GetAllyObjectBySlot(slotIndex);
AllyCombatant ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
teamUIController.Instance?.SwapAllyWithLowerAdjacent(slotIndex);
if (ally != null && !ally.IsDead)
{
ally.scoreEfficiency += 0.012f;
}
return true;
}
private bool HandleEquipSkillSocialMaskStart(int slotIndex, AllyCombatant ally)
{
ActivateSocialMask(slotIndex, ally);
return true;
}
private void ActivateSocialMask(int slotIndex, AllyCombatant ally)
{
if (ally == null || ally.IsDead) return;
if (_equipSocialMaskActiveSlots.Contains(slotIndex)) return;
_equipSocialMaskActiveSlots.Add(slotIndex);
_equipSocialMaskOriginalMaxHpBySlot[slotIndex] = ally.maxHP;
_equipSocialMaskOriginalScoreBySlot[slotIndex] = ally.scoreEfficiency;
int newMaxHp = Mathf.Max(1, Mathf.FloorToInt(ally.maxHP * 0.20f));
ally.SetMaxHP(newMaxHp, false);
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency * 0.60f);
}
private bool HandleEquipSkillSocialMaskKill(int slotIndex, AllyCombatant ally)
{
if (ally == null || ally.IsDead) return true;
if (!_equipSocialMaskActiveSlots.Contains(slotIndex)) return true;
ally.ModifyAttack(2);
return true;
}
private bool HandleEquipSkillSocialMaskClear(int slotIndex, AllyCombatant ally)
{
DeactivateSocialMask(slotIndex, ally);
return true;
}
private void DeactivateSocialMask(int slotIndex, AllyCombatant ally)
{
if (ally == null) return;
if (!_equipSocialMaskActiveSlots.Remove(slotIndex)) return;
int currentAttack = ally.attack;
if (_equipSocialMaskOriginalMaxHpBySlot.TryGetValue(slotIndex, out int originalMaxHp))
{
ally.SetMaxHP(Mathf.Max(1, originalMaxHp), false);
ally.SetCurrentHP(ally.maxHP, true);
}
float baseScore = ally.scoreEfficiency;
if (_equipSocialMaskOriginalScoreBySlot.TryGetValue(slotIndex, out float originalScore))
{
baseScore = originalScore;
}
ally.SetAttack(0);
ally.scoreEfficiency = Mathf.Max(0f, baseScore + currentAttack * 0.01f);
_equipSocialMaskOriginalMaxHpBySlot.Remove(slotIndex);
_equipSocialMaskOriginalScoreBySlot.Remove(slotIndex);
}
private bool HandleEquipSkillQuietTurn(AllyCombatant ally)
{
if (!ally.lastManaSpendWasFullBar || ally.lastManaSpentAmount <= 0) return true;
int selfHeal = Mathf.CeilToInt(ally.maxHP * 0.01f);
if (selfHeal > 0)
{
ally.ModifyHP(selfHeal, true);
}
return true;
}
private bool HandleEquipSkillQuietTurnHealAdjacent(AllyCombatant ally)
{
if (ally == null || ally.IsDead) return true;
if (ally.lastHealActualAmount <= 0) return true;
if (teamUIController.Instance == null) return true;
if (_equipQuietTurnBroadcastInProgress) return true;
_equipQuietTurnBroadcastInProgress = true;
try
{
int[] adjacent = teamUIController.Instance.GetAdjacentAllyIndices(ally.slotIndex);
for (int i = 0; i < adjacent.Length; i++)
{
GameObject allyGo = GetAllyObjectBySlot(adjacent[i]);
AllyCombatant adj = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (adj == null || adj.IsDead) continue;
int manaGain = Mathf.CeilToInt(Mathf.Max(0, ally.attack) * 0.30f);
if (manaGain > 0)
{
adj.ModifyMana(manaGain, true, true);
}
}
}
finally
{
_equipQuietTurnBroadcastInProgress = false;
}
return true;
}
private bool HandleEquipSkillBlock(AllyCombatant ally)
{
int healAmount = Mathf.CeilToInt(ally.maxHP * 0.10f);
if (teamUIController.Instance != null)
{
int[] adjacent = teamUIController.Instance.GetAdjacentAllyIndices(ally.slotIndex);
for (int i = 0; i < adjacent.Length; i++)
{
GameObject allyGo = GetAllyObjectBySlot(adjacent[i]);
AllyCombatant adj = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (adj == null || adj.IsDead) continue;
if (healAmount > 0) adj.ModifyHP(healAmount, true);
}
}
return true;
}
private bool HandleEquipSkillBlockStart(AllyCombatant ally)
{
int increase = Mathf.CeilToInt(ally.maxHP * 0.05f);
if (increase > 0)
{
ally.SetMaxHP(ally.maxHP + increase, false);
}
return true;
}
private bool HandleEquipSkillFutureStart(int slotIndex)
{
_equipFutureActiveSlots.Add(slotIndex);
return true;
}
private bool HandleEquipSkillFutureHeal(int slotIndex, AllyCombatant ally)
{
if (ally == null || ally.IsDead) return true;
if (!_equipFutureActiveSlots.Contains(slotIndex)) return true;
if (teamUIController.Instance == null) return true;
int healedAmount = Mathf.Max(0, ally.lastHealActualAmount);
if (healedAmount <= 0) return true;
int bonusMaxHp = Mathf.CeilToInt(healedAmount * 0.10f);
if (bonusMaxHp <= 0) return true;
int[] adjacent = teamUIController.Instance.GetAdjacentAllyIndices(slotIndex);
for (int i = 0; i < adjacent.Length; i++)
{
GameObject allyGo = GetAllyObjectBySlot(adjacent[i]);
AllyCombatant adj = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (adj == null || adj.IsDead) continue;
adj.SetMaxHP(adj.maxHP + bonusMaxHp, false);
}
return true;
}
private bool HandleEquipSkillFutureClear(int slotIndex, AllyCombatant ally)
{
if (_equipFutureActiveSlots.Remove(slotIndex) && ally != null && !ally.IsDead)
{
ally.scoreEfficiency += 0.02f;
}
return true;
}
private bool HandleIllusionSkillBole14StartMaxHp(AllyCombatant ally)
{
int increase = Mathf.CeilToInt(ally.maxHP * 0.02f);
@@ -2386,6 +3147,51 @@ ResolvedGroup:
_equipManaFullSpendCountBySlot.Clear();
_equipGhoulSchoolActiveSlots.Clear();
_equipGhoulSchoolSeenSkillIdsBySlot.Clear();
foreach (var kv in _equipThousandthAppliedBonusBySlot)
{
var allyGo = GetAllyObjectBySlot(kv.Key);
var ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally != null)
{
ally.scoreEfficiency -= kv.Value;
}
}
_equipThousandthActiveSlots.Clear();
_equipThousandthAppliedBonusBySlot.Clear();
_equipForgetfulRhythmActiveSlots.Clear();
_equipForgetfulRhythmSeenSkillIdsBySlot.Clear();
_equipForgetfulRhythmStacksBySlot.Clear();
foreach (var kv in _equipTalentScoutDisagreeCoroutinesBySlot)
{
if (kv.Value != null) StopCoroutine(kv.Value);
}
foreach (var kv in _equipTalentScoutDisagreeRestoreScoreBySlot)
{
var allyGo = GetAllyObjectBySlot(kv.Key);
var ally = allyGo != null ? allyGo.GetComponent<AllyCombatant>() : null;
if (ally != null) ally.scoreEfficiency = Mathf.Max(0f, kv.Value);
}
_equipTalentScoutLastBorrowedSkillBySlot.Clear();
_equipTalentScoutDisagreeCoroutinesBySlot.Clear();
_equipTalentScoutDisagreeRestoreScoreBySlot.Clear();
_equipOutOfLineKillCountsBySlot.Clear();
_equipStormStacksBySlot.Clear();
_equipStormProcessingSlots.Clear();
_equipFateRelianceActiveSlots.Clear();
foreach (var kv in _equipNewIdeaExpireCoroutinesBySlot)
{
if (kv.Value == null) continue;
for (int i = 0; i < kv.Value.Count; i++)
{
if (kv.Value[i] != null) StopCoroutine(kv.Value[i]);
}
}
_equipNewIdeaExpireCoroutinesBySlot.Clear();
_equipNewIdeaStacksBySlot.Clear();
_equipSocialMaskActiveSlots.Clear();
_equipSocialMaskOriginalMaxHpBySlot.Clear();
_equipSocialMaskOriginalScoreBySlot.Clear();
_equipFutureActiveSlots.Clear();
int slots = teamUIController.Instance.allySlotIds.Count;
for (int i = 0; i < slots; i++)
{
@@ -2783,6 +3589,8 @@ ResolvedGroup:
}
}
HandleEquipNoteJudgePassives(trackIndex, judgeResult);
bool anyTriggered = false;
// If equipped groups exist, iterate equipped groups and check each skill for OnNoteHit
@@ -3281,3 +4089,6 @@ ResolvedGroup:
+2
View File
@@ -51,6 +51,8 @@ public class SkillDefinition : ScriptableObject
[Header("Inspector")]
[Tooltip("Documentation text normalized.")]
public string skillId;
[Tooltip("Optional obsession tag used by player memory skill mismatch checks. Leave empty to ignore.")]
public string obsessionTag;
[Tooltip("Documentation text normalized.")]
public string displayName;