很多更新,服务端连接,新UI和系统
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user