技能主要更新,修复卡顿并加入动画,以及各种其他更新。

This commit is contained in:
FloatGaming
2026-02-07 21:05:17 +08:00
parent abeca51be5
commit 1ac3cd0104
1349 changed files with 1526749 additions and 24850 deletions
+146 -54
View File
@@ -3,14 +3,14 @@ using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// SkillBuilder: жɿٵõļܺ
/// SkillBuilder: жɿٵõļܺ?
/// - ÿΪһ public ܱҪʩ source, Ŀ specificTarget ȣ
/// - ڲ EffectSystem.Instance.ApplyEffect(...)ͳһʹ Selector EffectType
/// - òamount, duration, tickIntervalԲУ
///
/// ÷ʾ:
/// SkillBuilder.Instance.ExecuteSkill("Fireball", EffectType.DamageSingleEnemy, 120f, Selector.CurrentEnemies, caster, target);
/// SkillBuilder.Instance.ApplyScoreMultiplier(caster, Selector.AllAllies, 1.5f, 5f); // 5 ڶ÷ֳ 1.5
/// SkillBuilder.Instance.ApplyScoreMultiplier(caster, Selector.AllAllies, 1.5f, 5f); // 5 ڶ÷ֳ?1.5
/// </summary>
public class SkillBuilder : MonoBehaviour
{
@@ -64,6 +64,12 @@ public class SkillBuilder : MonoBehaviour
private Dictionary<int, AllyHero_SO> _allyHeroSoById;
private Dictionary<int, AllyHero_SO> _allyHeroSoBySlotCache;
// Reusable buffers to reduce allocations during gameplay
private readonly Dictionary<string, float> _varsBuffer = new Dictionary<string, float>(16);
private readonly List<string> _tmpNoteIdRemoval = new List<string>(16);
private int _cachedAlliesFrame = -1;
private readonly List<GameObject> _cachedAllies = new List<GameObject>(8);
private void PrewarmAllyHeroSOIndex()
{
if (_allAllyHeroSOs != null && _allAllyHeroSOs.Length > 0) return;
@@ -85,7 +91,7 @@ public class SkillBuilder : MonoBehaviour
if (GameConfig.verboseLogs)
{
Debug.Log($"[SkillBuilder] PrewarmAllyHeroSOIndex: loaded {_allAllyHeroSOs?.Length ?? 0} AllyHero_SO assets, indexed {_allyHeroSoById.Count}");
LogVerbose($"[SkillBuilder] PrewarmAllyHeroSOIndex: loaded {_allAllyHeroSOs?.Length ?? 0} AllyHero_SO assets, indexed {_allyHeroSoById.Count}");
}
}
@@ -101,7 +107,7 @@ public class SkillBuilder : MonoBehaviour
}
// Apply as single-instance damage to current enemies; EffectSystem will pick first applicable enemy for DamageSingleEnemy
global::EffectSystem.Instance.ApplyEffect(Selector.CurrentEnemies, EffectType.DamageSingleEnemy, amount, 0f, caster, null);
Debug.Log($"[SkillBuilder] DealDamageFromAllyToEnemies: applied {amount} from slot {slotIndex} via EffectSystem");
LogVerbose($"[SkillBuilder] DealDamageFromAllyToEnemies: applied {amount} from slot {slotIndex} via EffectSystem");
}
private void ApplySharedOnNoteHit(int trackIndex, string judgeResult)
@@ -140,7 +146,7 @@ public class SkillBuilder : MonoBehaviour
if (ally != null && manaGain != 0)
{
ally.ModifyMana(manaGain, true, false);
Debug.Log($"[SkillBuilder] Applied mana gain {manaGain} to slot {trackIndex} due to judge {judgeResult}");
LogVerbose($"[SkillBuilder] Applied mana gain {manaGain} to slot {trackIndex} due to judge {judgeResult}");
}
if (judgeResult == "Miss")
@@ -151,7 +157,7 @@ public class SkillBuilder : MonoBehaviour
// apply damage reduction by damageResistance: final loss = missBase * (1 - damageResistance)
float loss = missBase * (1f - ally.damageResistance);
ally.ModifyHP(-Mathf.CeilToInt(loss), true);
Debug.Log($"[SkillBuilder] Applied miss HP loss {loss} to slot {trackIndex} (damageResistance={ally.damageResistance})");
LogVerbose($"[SkillBuilder] Applied miss HP loss {loss} to slot {trackIndex} (damageResistance={ally.damageResistance})");
}
}
else
@@ -167,7 +173,7 @@ public class SkillBuilder : MonoBehaviour
}
// ----------------------------- ͨüִнӿ -----------------------------
// ִͨУ, Ч, ֵ, Ŀѡ, ʩ, ѡĿ, ʱ tick
// ִͨУ, Ч, ֵ, Ŀѡ, ʩ, ѡĿ, ʱ?tick
public void ExecuteSkill(string skillName, EffectType effectType, float amount, Selector selector, GameObject caster, GameObject specificTarget = null, float duration = 0f, float tickInterval = 1f)
{
if (global::EffectSystem.Instance == null)
@@ -176,7 +182,7 @@ public class SkillBuilder : MonoBehaviour
return;
}
// basic logging
Debug.Log($"[SkillBuilder] ExecuteSkill: {skillName} type={effectType} amount={amount} selector={selector} caster={(caster?caster.name:"null")} target={(specificTarget?specificTarget.name:"null")} duration={duration}");
LogVerbose($"[SkillBuilder] ExecuteSkill: {skillName} type={effectType} amount={amount} selector={selector} caster={(caster?caster.name:"null")} target={(specificTarget?specificTarget.name:"null")} duration={duration}");
// additional debug: show when caster is null which will make Selector.Self produce no targets
if (selector == Selector.Self && caster == null)
@@ -196,7 +202,51 @@ public class SkillBuilder : MonoBehaviour
ExecuteSkill(skillName, effectType, amount, selector, casterGO, targetGO, duration, tickInterval);
}
// ----------------------------- Ŀűʹã EffectSystem Ľһ£ -----------------------------
private void LogVerbose(string message)
{
if (GameConfig.verboseLogs) Debug.Log(message);
}
private List<GameObject> GetAlliesCached()
{
if (_cachedAlliesFrame == Time.frameCount) return _cachedAllies;
_cachedAlliesFrame = Time.frameCount;
_cachedAllies.Clear();
var ui = teamUIController.Instance;
if (ui != null)
{
int count = ui.allySlotIds != null ? ui.allySlotIds.Count : 5;
for (int i = 0; i < count; i++)
{
var go = ui.GetAllyObjectBySlot(i);
if (go != null && !_cachedAllies.Contains(go)) _cachedAllies.Add(go);
}
}
if (_cachedAllies.Count == 0)
{
for (int i = 1; i <= 5; i++)
{
var go = GameObject.Find($"ally_0{i}");
if (go != null && !_cachedAllies.Contains(go)) _cachedAllies.Add(go);
}
}
try
{
var tagged = GameObject.FindGameObjectsWithTag("Ally");
foreach (var g in tagged)
{
if (g != null && !_cachedAllies.Contains(g)) _cachedAllies.Add(g);
}
}
catch { }
return _cachedAllies;
}
// ----------------------------- Ŀűʹã?EffectSystem Ľһ£ -----------------------------
// ط selector GameObject бܽűвֱӸ ICombatant.Buff
public List<GameObject> ResolveTargetsLocal(Selector selector, GameObject source = null, GameObject specificTarget = null)
{
@@ -213,12 +263,16 @@ public class SkillBuilder : MonoBehaviour
if (source != null) list.Add(source);
break;
case Selector.AllAllies:
for (int i = 1; i <= 5; i++) { var go = GameObject.Find($"ally_0{i}"); if (go != null) list.Add(go); }
try { var tagged = GameObject.FindGameObjectsWithTag("Ally"); foreach (var g in tagged) if (!list.Contains(g)) list.Add(g); } catch { }
foreach (var go in GetAlliesCached())
{
if (go != null) list.Add(go);
}
break;
case Selector.AllAlliesExceptSelf:
for (int i = 1; i <= 5; i++) { var go = GameObject.Find($"ally_0{i}"); if (go != null) list.Add(go); }
try { var tagged = GameObject.FindGameObjectsWithTag("Ally"); foreach (var g in tagged) if (!list.Contains(g)) list.Add(g); } catch { }
foreach (var go in GetAlliesCached())
{
if (go != null) list.Add(go);
}
if (source != null) list.RemoveAll(g => g == null || g == source);
break;
case Selector.AdjacentAllies:
@@ -275,8 +329,10 @@ public class SkillBuilder : MonoBehaviour
else { try { var tagged = GameObject.FindGameObjectsWithTag("Enemy"); foreach (var g in tagged) if (!list.Contains(g)) list.Add(g); } catch { var single = GameObject.Find("thisEnemy"); if (single != null) list.Add(single); } }
break;
case Selector.AllEntities:
for (int i = 1; i <= 5; i++) { var go = GameObject.Find($"ally_0{i}"); if (go != null) list.Add(go); }
try { var tagged = GameObject.FindGameObjectsWithTag("Ally"); foreach (var g in tagged) if (!list.Contains(g)) list.Add(g); } catch { }
foreach (var go in GetAlliesCached())
{
if (go != null) list.Add(go);
}
try { var taggedE = GameObject.FindGameObjectsWithTag("Enemy"); foreach (var g in taggedE) if (!list.Contains(g)) list.Add(g); } catch { var single = GameObject.Find("thisEnemy"); if (single != null && !list.Contains(single)) list.Add(single); }
break;
}
@@ -379,7 +435,7 @@ public class SkillBuilder : MonoBehaviour
}
}
// ----------------------------- бݼʾ -----------------------------
// ----------------------------- бݼʾ?-----------------------------
public void DealSingleEnemyDamage(GameObject caster, GameObject enemyTarget, float amount)
{
if (enemyTarget == null) { Debug.LogWarning("DealSingleEnemyDamage: enemyTarget == null"); return; }
@@ -549,7 +605,7 @@ public class SkillBuilder : MonoBehaviour
return best;
}
// ȡӢۻʹõǰƥĵȼ attackû򷵻 0
// ȡӢۻʹõǰƥĵȼ?attackû򷵻 0
public int GetAllyBaseAttack(AllyHero_SO so)
{
if (so == null) return 0;
@@ -559,8 +615,8 @@ public class SkillBuilder : MonoBehaviour
return 0;
}
// ͳһֵ inputValue == -1 ʱʹ skillStatic + allyAttack
// ֱʹ inputValue EffectType Ҫת
// ͳһֵ?inputValue == -1 ʱʹ skillStatic + allyAttack
// ֱʹ inputValue?EffectType Ҫת
public float ComputeSkillValue(GameObject caster, int slotIndex, float inputValue, int skillStatic)
{
if (inputValue != -1f) return inputValue;
@@ -619,7 +675,7 @@ public class SkillBuilder : MonoBehaviour
if (alt != null)
{
caster = alt;
Debug.Log($"UseSkillDefinition: resolved caster via teamUIController for slot {slotIndex} -> {caster.name}");
LogVerbose($"UseSkillDefinition: resolved caster via teamUIController for slot {slotIndex} -> {caster.name}");
}
}
}
@@ -631,12 +687,12 @@ public class SkillBuilder : MonoBehaviour
if (byName != null)
{
caster = byName;
Debug.Log($"UseSkillDefinition: resolved caster via name ally_0{slotIndex + 1} -> {caster.name}");
LogVerbose($"UseSkillDefinition: resolved caster via name ally_0{slotIndex + 1} -> {caster.name}");
}
}
// Log useful debug info for diagnosing OnEnemyDead->Self issues
Debug.Log($"[SkillBuilder] UseSkillDefinition: casting skill {def.skillId} for slot {slotIndex} (caster={(caster?caster.name:"null")}) selector={def.defaultSelector} operateDirectly={def.operateDirectly} inputValue={inputValue} specificTarget={(specificTarget?specificTarget.name:"null")}");
LogVerbose($"[SkillBuilder] UseSkillDefinition: casting skill {def.skillId} for slot {slotIndex} (caster={(caster?caster.name:"null")}) selector={def.defaultSelector} operateDirectly={def.operateDirectly} inputValue={inputValue} specificTarget={(specificTarget?specificTarget.name:"null")}");
float amount = 0f;
// If caller provided explicit inputValue use it
@@ -648,7 +704,9 @@ public class SkillBuilder : MonoBehaviour
{
// Build variables from SO and caster; formula can be a plain number too
var so = GetAllyHeroSOBySlot(slotIndex);
var vars = new Dictionary<string, float>();
var casterAlly = caster != null ? caster.GetComponent<AllyCombatant>() : null;
var vars = _varsBuffer;
vars.Clear();
vars["slot"] = slotIndex;
vars["attack"] = GetAllyBaseAttack(so);
if (so != null && so.levelStats != null && so.levelStats.Count > 0)
@@ -660,6 +718,9 @@ public class SkillBuilder : MonoBehaviour
vars["damageResistance"] = lvl.damageResistance;
vars["scoreEfficiency"] = lvl.scoreEfficiency;
vars["attack"] = lvl.attack; // ensure attack from SO levelStats is available
vars["level"] = lvl.levelID;
vars["levelID"] = lvl.levelID;
vars["currentLevel"] = lvl.levelID;
}
else
{
@@ -667,9 +728,16 @@ public class SkillBuilder : MonoBehaviour
vars["maxMana"] = 0f;
vars["damageResistance"] = 0f;
vars["scoreEfficiency"] = 1f;
vars["level"] = 0f;
vars["levelID"] = 0f;
vars["currentLevel"] = 0f;
}
if (so != null) vars["ally_currentEXP"] = so.ally_currentEXP;
else vars["ally_currentEXP"] = 0f;
vars["currentMana"] = casterAlly != null ? casterAlly.currentMana : 0f;
vars["currentScore"] = casterAlly != null ? casterAlly.currentScore : 0f;
vars["idolScore"] = vars["currentScore"];
vars["currentHP"] = casterAlly != null ? casterAlly.currentHP : 0f;
if (string.IsNullOrWhiteSpace(def.formula))
{
@@ -723,6 +791,12 @@ public class SkillBuilder : MonoBehaviour
foreach (var t in targets)
{
if (t == null) continue;
if (GameConfig.skillDebugMode)
{
Debug.Log($"<color=#00FFFF>[SkillDebug]</color> <color=#FFD700>ID: {def.skillId}</color> | <color=#00FF00>Trigger: {def.triggerCondition}</color> | <color=#FFA500>Target: {t.name}</color> | <color=#EE82EE>Effect: {def.effectType}</color> | <color=#FF4500>Value: {amountTotal:F2}</color>");
}
var ally = t.GetComponent<AllyCombatant>();
var ic = t.GetComponent<ICombatant>();
@@ -811,13 +885,16 @@ public class SkillBuilder : MonoBehaviour
if (ally != null) ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - amountTotal);
break;
case EffectType.IncreaseAttack:
if (ally != null) ally.attack += Mathf.CeilToInt(amountTotal);
if (ally != null) ally.ModifyAttack(Mathf.CeilToInt(amountTotal));
else if (t.TryGetComponent<EnemyCombatant>(out var enemy5)) enemy5.attack += Mathf.CeilToInt(amountTotal);
break;
case EffectType.DecreaseAttack:
if (ally != null) ally.attack = Mathf.Max(0, ally.attack - Mathf.CeilToInt(amountTotal));
if (ally != null) ally.ModifyAttack(-Mathf.CeilToInt(amountTotal));
else if (t.TryGetComponent<EnemyCombatant>(out var enemy6)) enemy6.attack = Mathf.Max(0, enemy6.attack - Mathf.CeilToInt(amountTotal));
break;
case EffectType.RedirectNextDamageToSelf:
if (ally != null) AllyCombatant.ActivateNextDamageRedirect(ally, def.defaultDuration);
break;
default:
// non-direct path handled below, but keep compatibility
ExecuteSkill(def.displayName ?? def.skillId, def.effectType, amountTotal, def.defaultSelector, caster, specificTarget, def.defaultDuration, def.defaultTickInterval);
@@ -827,6 +904,10 @@ public class SkillBuilder : MonoBehaviour
}
else
{
if (GameConfig.skillDebugMode)
{
Debug.Log($"<color=#00FFFF>[SkillDebug]</color> <color=#FFD700>ID: {def.skillId}</color> | <color=#00FF00>Trigger: {def.triggerCondition}</color> | <color=#FFA500>Target: (Resolving via EffectSystem)</color> | <color=#EE82EE>Effect: {def.effectType}</color> | <color=#FF4500>Value: {amountTotal:F2}</color>");
}
// For non-direct path, EffectSystem expects 'amount' to be total amount for sustained effects
ExecuteSkill(def.displayName ?? def.skillId, def.effectType, amountTotal, def.defaultSelector, caster, specificTarget, def.defaultDuration, def.defaultTickInterval);
}
@@ -895,7 +976,7 @@ public class SkillBuilder : MonoBehaviour
var s = group.skills[i];
if (s == null) continue;
UseSkillDefinition(s, slotIndex, inputValue, specificTarget);
Debug.Log($"[SkillBuilder] UseSkillGroupForSlot: cast skill {s.skillId} from group '{group.groupName}' for slot {slotIndex}");
LogVerbose($"[SkillBuilder] UseSkillGroupForSlot: cast skill {s.skillId} from group '{group.groupName}' for slot {slotIndex}");
}
}
@@ -923,7 +1004,7 @@ public class SkillBuilder : MonoBehaviour
if (sk == null) continue;
if (sk.triggerCondition == SkillDefinition.SkillTrigger.OnGameStart)
{
Debug.Log($"[SkillBuilder] TriggerOnGameStart: casting equipped group skill {sk.skillId} for slot {i}");
LogVerbose($"[SkillBuilder] TriggerOnGameStart: casting equipped group skill {sk.skillId} for slot {i}");
UseSkillDefinition(sk, i, -1f, null);
}
}
@@ -940,7 +1021,7 @@ public class SkillBuilder : MonoBehaviour
if (sk == null) continue;
if (sk.triggerCondition == SkillDefinition.SkillTrigger.OnGameStart)
{
Debug.Log($"[SkillBuilder] TriggerOnGameStart: casting group skill {sk.skillId} for slot {i}");
LogVerbose($"[SkillBuilder] TriggerOnGameStart: casting group skill {sk.skillId} for slot {i}");
UseSkillDefinition(sk, i, -1f, null);
}
}
@@ -951,7 +1032,7 @@ public class SkillBuilder : MonoBehaviour
if (def == null) continue;
if (def.triggerCondition == SkillDefinition.SkillTrigger.OnGameStart)
{
Debug.Log($"[SkillBuilder] TriggerOnGameStart: casting primary skill {def.skillId} for slot {i}");
LogVerbose($"[SkillBuilder] TriggerOnGameStart: casting primary skill {def.skillId} for slot {i}");
UsePrimarySkillForSlot(i, -1f, null);
}
}
@@ -985,7 +1066,7 @@ public class SkillBuilder : MonoBehaviour
{
if (def == null) continue;
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnEnemyDead) continue;
Debug.Log($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting skill {def.skillId}");
LogVerbose($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting skill {def.skillId}");
// Only pass deadEnemy as specificTarget when the skill truly expects a specific target (e.g. targets CurrentEnemies)
GameObject ctxTarget = null;
if (deadEnemy != null && (def.requiresSpecificTarget || def.defaultSelector == Selector.CurrentEnemies)) ctxTarget = deadEnemy.gameObject;
@@ -1002,7 +1083,7 @@ public class SkillBuilder : MonoBehaviour
{
if (def == null) continue;
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnEnemyDead) continue;
Debug.Log($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting skill {def.skillId}");
LogVerbose($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting skill {def.skillId}");
GameObject ctxTarget = null;
if (deadEnemy != null && (def.requiresSpecificTarget || def.defaultSelector == Selector.CurrentEnemies)) ctxTarget = deadEnemy.gameObject;
UseSkillDefinition(def, i, -1f, ctxTarget);
@@ -1013,7 +1094,7 @@ public class SkillBuilder : MonoBehaviour
var primary = so.GetPrimarySkill();
if (primary != null && primary.triggerCondition == SkillDefinition.SkillTrigger.OnEnemyDead)
{
Debug.Log($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting primary skill {primary.skillId}");
LogVerbose($"[SkillBuilder] TriggerOnEnemyDead: slot {i+1} casting primary skill {primary.skillId}");
// Only pass deadEnemy as specificTarget when the skill truly expects a specific target (e.g. targets CurrentEnemies)
GameObject ctxTarget = null;
if (deadEnemy != null && (primary.requiresSpecificTarget || primary.defaultSelector == Selector.CurrentEnemies)) ctxTarget = deadEnemy.gameObject;
@@ -1049,7 +1130,7 @@ public class SkillBuilder : MonoBehaviour
{
if (def == null) continue;
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnEnemyRevive) continue;
Debug.Log($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting skill {def.skillId}");
LogVerbose($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting skill {def.skillId}");
UseSkillDefinition(def, i, -1f, enemy != null ? enemy.gameObject : null);
}
}
@@ -1063,7 +1144,7 @@ public class SkillBuilder : MonoBehaviour
{
if (def == null) continue;
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnEnemyRevive) continue;
Debug.Log($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting skill {def.skillId} (from primary group)");
LogVerbose($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting skill {def.skillId} (from primary group)");
UseSkillDefinition(def, i, -1f, enemy != null ? enemy.gameObject : null);
}
continue;
@@ -1072,7 +1153,7 @@ public class SkillBuilder : MonoBehaviour
var primary2 = so.GetPrimarySkill();
if (primary2 != null && primary2.triggerCondition == SkillDefinition.SkillTrigger.OnEnemyRevive)
{
Debug.Log($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting primary skill {primary2.skillId}");
LogVerbose($"[SkillBuilder] TriggerOnEnemyRevive: slot {i+1} casting primary skill {primary2.skillId}");
UsePrimarySkillForSlot(i, -1f, enemy != null ? enemy.gameObject : null);
}
}
@@ -1137,10 +1218,11 @@ public class SkillBuilder : MonoBehaviour
// noteType indicates whether the hit event came from a Tap or Hold (tail)
public bool NotifyNoteHit(int trackIndex, string judgeResult, SkillDefinition.NoteTypeTrigger noteType = SkillDefinition.NoteTypeTrigger.Tap, string uniqueNoteId = null)
{
Debug.Log($"[SkillBuilder] NotifyNoteHit RECEIVED: track={trackIndex} judge={judgeResult} noteType={noteType} uniqueId={uniqueNoteId}");
LogVerbose($"[SkillBuilder] NotifyNoteHit RECEIVED: track={trackIndex} judge={judgeResult} noteType={noteType} uniqueId={uniqueNoteId}");
// cleanup old processed ids (> 10s)
float nowCleanup = Time.time;
var toRemove = new List<string>();
_tmpNoteIdRemoval.Clear();
var toRemove = _tmpNoteIdRemoval;
foreach (var kv in _processedNoteHitTimestamps)
{
if (nowCleanup - kv.Value > 10f) toRemove.Add(kv.Key);
@@ -1167,21 +1249,21 @@ public class SkillBuilder : MonoBehaviour
try { ApplySharedOnNoteHit(trackIndex, judgeResult); } catch (System.Exception ex) { Debug.LogError($"[SkillBuilder] ApplySharedOnNoteHit threw: {ex}"); }
}
if (trackIndex < 0) { Debug.Log("[SkillBuilder] NotifyNoteHit: invalid trackIndex"); return false; }
if (trackIndex < 0) { LogVerbose("[SkillBuilder] NotifyNoteHit: invalid trackIndex"); return false; }
var so = GetAllyHeroSOBySlot(trackIndex);
if (so == null)
{
Debug.Log($"[SkillBuilder] NotifyNoteHit: no SO found for slot {trackIndex}, attempting fallback resolution...");
LogVerbose($"[SkillBuilder] NotifyNoteHit: no SO found for slot {trackIndex}, attempting fallback resolution...");
// Dump current UI slot ids for diagnostics
if (teamUIController.Instance != null && teamUIController.Instance.allySlotIds != null)
{
Debug.Log($"[SkillBuilder] teamUIController.allySlotIds = [{string.Join(",", teamUIController.Instance.allySlotIds)}]");
LogVerbose($"[SkillBuilder] teamUIController.allySlotIds = [{string.Join(",", teamUIController.Instance.allySlotIds)}]");
}
// Try to find ally GameObject named by convention
var foundGO = GameObject.Find($"ally_0{trackIndex + 1}");
if (foundGO != null)
{
Debug.Log($"[SkillBuilder] Found GameObject by name ally_0{trackIndex + 1}: {foundGO.name}");
LogVerbose($"[SkillBuilder] Found GameObject by name ally_0{trackIndex + 1}: {foundGO.name}");
// try to map to UI slot
var ui = teamUIController.Instance;
if (ui != null && ui.allySlotIds != null)
@@ -1192,7 +1274,7 @@ public class SkillBuilder : MonoBehaviour
if (slotObj == null) continue;
if (slotObj == foundGO || foundGO.transform.IsChildOf(slotObj.transform))
{
Debug.Log($"[SkillBuilder] Resolved fallback slot {i} for GameObject {foundGO.name}");
LogVerbose($"[SkillBuilder] Resolved fallback slot {i} for GameObject {foundGO.name}");
trackIndex = i; // override
so = GetAllyHeroSOBySlot(trackIndex);
break;
@@ -1202,7 +1284,7 @@ public class SkillBuilder : MonoBehaviour
}
if (so == null)
{
Debug.Log($"[SkillBuilder] NotifyNoteHit: still no SO for resolved slot {trackIndex}, aborting NotifyNoteHit");
LogVerbose($"[SkillBuilder] NotifyNoteHit: still no SO for resolved slot {trackIndex}, aborting NotifyNoteHit");
return false;
}
}
@@ -1252,7 +1334,7 @@ public class SkillBuilder : MonoBehaviour
}
// trigger
Debug.Log($"[SkillBuilder] NotifyNoteHit: triggering equipped skill {def.skillId} for slot {trackIndex} due to note hit {judgeResult} (noteType={noteType})");
LogVerbose($"[SkillBuilder] NotifyNoteHit: triggering equipped skill {def.skillId} for slot {trackIndex} due to note hit {judgeResult} (noteType={noteType})");
UseSkillDefinition(def, trackIndex, -1f, null);
_lastOnNoteHitTriggerTime[key] = now;
anyTriggered = true;
@@ -1263,10 +1345,10 @@ public class SkillBuilder : MonoBehaviour
// Fallback to previous behavior using primary skill (availableSkills)
var defPrimary = so.GetPrimarySkill();
if (defPrimary == null) { Debug.Log($"[SkillBuilder] NotifyNoteHit: no primary skill for slot {trackIndex}"); return false; }
if (defPrimary == null) { LogVerbose($"[SkillBuilder] NotifyNoteHit: no primary skill for slot {trackIndex}"); return false; }
if (defPrimary.triggerCondition != SkillDefinition.SkillTrigger.OnNoteHit)
{
Debug.Log($"[SkillBuilder] NotifyNoteHit: primary skill {defPrimary.skillId} for slot {trackIndex} is not set to OnNoteHit (actual={defPrimary.triggerCondition}), rejecting");
LogVerbose($"[SkillBuilder] NotifyNoteHit: primary skill {defPrimary.skillId} for slot {trackIndex} is not set to OnNoteHit (actual={defPrimary.triggerCondition}), rejecting");
return false;
}
@@ -1276,10 +1358,10 @@ public class SkillBuilder : MonoBehaviour
switch (defPrimary.noteTriggerType)
{
case SkillDefinition.NoteTypeTrigger.Tap:
if (effectiveNoteTypePrimary != SkillDefinition.NoteTypeTrigger.Tap) { Debug.Log($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires Tap but event is {noteType}, rejecting"); return false; }
if (effectiveNoteTypePrimary != SkillDefinition.NoteTypeTrigger.Tap) { LogVerbose($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires Tap but event is {noteType}, rejecting"); return false; }
break;
case SkillDefinition.NoteTypeTrigger.Hold:
if (noteType != SkillDefinition.NoteTypeTrigger.Hold) { Debug.Log($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires Hold but event is {noteType}, rejecting"); return false; }
if (noteType != SkillDefinition.NoteTypeTrigger.Hold) { LogVerbose($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires Hold but event is {noteType}, rejecting"); return false; }
break;
case SkillDefinition.NoteTypeTrigger.Either:
break;
@@ -1288,12 +1370,12 @@ public class SkillBuilder : MonoBehaviour
int qualityPrimary = JudgeQualityFromString(judgeResult);
if (defPrimary.onNoteHitMinThreshold == SkillDefinition.NoteTriggerThreshold.Miss)
{
if (qualityPrimary != 0) { Debug.Log($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} is Miss-only but quality={qualityPrimary}, rejecting"); return false; }
if (qualityPrimary != 0) { LogVerbose($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} is Miss-only but quality={qualityPrimary}, rejecting"); return false; }
}
else
{
int required = (int)defPrimary.onNoteHitMinThreshold;
if (qualityPrimary < required) { Debug.Log($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires quality>={required} but got {qualityPrimary}, rejecting"); return false; }
if (qualityPrimary < required) { LogVerbose($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} requires quality>={required} but got {qualityPrimary}, rejecting"); return false; }
}
string keyPrimary = $"{trackIndex}:{defPrimary.skillId}";
@@ -1302,12 +1384,12 @@ public class SkillBuilder : MonoBehaviour
{
if (nowPrimary - lastPrimary < defPrimary.onNoteHitCooldown)
{
Debug.Log($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} on cooldown for slot {trackIndex}");
LogVerbose($"[SkillBuilder] NotifyNoteHit: skill {defPrimary.skillId} on cooldown for slot {trackIndex}");
return false;
}
}
Debug.Log($"[SkillBuilder] NotifyNoteHit: triggering primary skill {defPrimary.skillId} for slot {trackIndex} due to note hit {judgeResult} (noteType={noteType})");
LogVerbose($"[SkillBuilder] NotifyNoteHit: triggering primary skill {defPrimary.skillId} for slot {trackIndex} due to note hit {judgeResult} (noteType={noteType})");
UsePrimarySkillForSlot(trackIndex, -1f, null);
_lastOnNoteHitTriggerTime[keyPrimary] = nowPrimary;
return true;
@@ -1323,8 +1405,8 @@ public class SkillBuilder : MonoBehaviour
- EnemyTarget: if your skill should hit a specific enemy instance, supply that enemy's GameObject (for example from Enemy spawn manager or from collision detection). If you want to apply to all enemies, pass null and use Selector.CurrentEnemies.
*/
// ----------------------------- ʾΪ3 װһݺûʾ -----------------------------
// 3(2) ļ1ѭûǩ_ally03skill01(EffectType, value, Selector, duration)
// ----------------------------- ʾΪ? װһݺûʾ?-----------------------------
// ?(2) ļ1ѭûǩ_ally03skill01(EffectType, value, Selector, duration)
public void _ally03skill01(EffectType effectType, float value, Selector selector, float duration = 0f, GameObject specificTarget = null)
{
AllySlotSkill(2, "skill01", effectType, value, selector, duration, specificTarget);
@@ -1423,3 +1505,13 @@ public class SkillBuilder : MonoBehaviour
}
}
}