技能主要更新,修复卡顿并加入动画,以及各种其他更新。
This commit is contained in:
@@ -11,7 +11,7 @@ using TMPro;
|
||||
/// ʵ�� ICombatant �ӿ��Թ� EffectSystem ���á�
|
||||
/// - �� teamUIController ��ȡ UI ���ò�ͬ����ʾѪ��/����
|
||||
/// - ����ʱ�� TeamCharacterDataInfo �� AllyHero_SO ����������/�����Ȼ�������
|
||||
/// - ��ʼ����ֵΪ�������ֵ����ʼ����Ϊ 0
|
||||
/// - ��ʼ����ֵΪ�������ֵ����ʼ�����?0
|
||||
/// </summary>
|
||||
public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
{
|
||||
@@ -28,8 +28,12 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
private bool isDead = false; // new: dead flag
|
||||
public BeatmapManager bmm;
|
||||
|
||||
// --- Damage redirect (next hit) ---
|
||||
private static AllyCombatant s_nextDamageRedirector;
|
||||
private static float s_redirectExpireTime = -1f;
|
||||
|
||||
// add attack stat to be available at runtime
|
||||
[Tooltip("����������������ʱʹ�ã������ AllyHero_SO.levelStats[0].attack ��䣨������ڣ�")]
|
||||
[Tooltip("����������������ʱʹ�ã������?AllyHero_SO.levelStats[0].attack ��䣨������ڣ�")]
|
||||
public int attack = 0;
|
||||
|
||||
// scoring fields
|
||||
@@ -47,10 +51,10 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
[Tooltip("Score multiplier for Miss (e.g. 0)")]
|
||||
public float missRatio = 0f;
|
||||
|
||||
[Tooltip("��ɫ��ǰ����÷֣��ۼӣ�����ʾ�� UI �� currentScore �ı���")]
|
||||
[Tooltip("��ɫ��ǰ����÷֣��ۼӣ�����ʾ��?UI �� currentScore �ı���")]
|
||||
public int currentScore = 0;
|
||||
|
||||
[Tooltip("����������ޣ�����Ϊ int.MaxValue - 1")]
|
||||
[Tooltip("����������ޣ������?int.MaxValue - 1")]
|
||||
public int maxTrackScore = int.MaxValue - 1;
|
||||
|
||||
// When true, InitializeStatsFromData() will overwrite inspector values on Start.
|
||||
@@ -78,6 +82,14 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
// Track applied reversible effects for HP-percent skills so we can revert them on leave
|
||||
private Dictionary<string, AppliedEffect> _appliedHpPercentEffects = new Dictionary<string, AppliedEffect>();
|
||||
|
||||
// Track skills that are currently active because Attack == 0
|
||||
private HashSet<string> _activeAttackZeroSkills = new HashSet<string>();
|
||||
private Dictionary<string, AppliedEffect> _appliedAttackZeroEffects = new Dictionary<string, AppliedEffect>();
|
||||
|
||||
// Reusable buffers to reduce allocations during skill evaluation
|
||||
private readonly List<SkillDefinition> _tmpSkillDefs = new List<SkillDefinition>(16);
|
||||
private readonly Dictionary<string, float> _formulaVarsBuffer = new Dictionary<string, float>(16);
|
||||
|
||||
private class AppliedEffect
|
||||
{
|
||||
public EffectType effectType;
|
||||
@@ -91,6 +103,13 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
gameObject.name = $"ally_0{Mathf.Clamp(slotIndex + 1, 1, 5)}";
|
||||
if (_activeHpPercentSkills == null) _activeHpPercentSkills = new HashSet<string>();
|
||||
if (_appliedHpPercentEffects == null) _appliedHpPercentEffects = new Dictionary<string, AppliedEffect>();
|
||||
if (_activeAttackZeroSkills == null) _activeAttackZeroSkills = new HashSet<string>();
|
||||
if (_appliedAttackZeroEffects == null) _appliedAttackZeroEffects = new Dictionary<string, AppliedEffect>();
|
||||
}
|
||||
|
||||
private void LogVerbose(string message)
|
||||
{
|
||||
if (GameConfig.verboseLogs) Debug.Log(message);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -166,6 +185,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (Application.isPlaying && SkillBuilder.Instance != null)
|
||||
{
|
||||
EvaluateHPPercentageTriggers(currentHP);
|
||||
EvaluateAttackZeroTriggers();
|
||||
TryCastOnFullMana();
|
||||
}
|
||||
}
|
||||
@@ -173,7 +193,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
private void ResolveUIReferences()
|
||||
{
|
||||
// Try singleton first, fallback to scene search (works in editor)
|
||||
var ui = teamUIController.Instance ?? FindObjectOfType<teamUIController>();
|
||||
var ui = teamUIController.Instance != null ? teamUIController.Instance : UnityEngine.Object.FindAnyObjectByType<teamUIController>();
|
||||
if (ui == null) return;
|
||||
switch (slotIndex)
|
||||
{
|
||||
@@ -261,7 +281,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
case 3: ui.teammate04_current_scoreText = tmp; break;
|
||||
case 4: ui.teammate05_current_scoreText = tmp; break;
|
||||
}
|
||||
Debug.Log($"[AllyCombatant] Resolved currentScoreText for slot {slotIndex+1} from parent {parent.name} -> {tmp.gameObject.name}");
|
||||
LogVerbose($"[AllyCombatant] Resolved currentScoreText for slot {slotIndex+1} from parent {parent.name} -> {tmp.gameObject.name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -325,8 +345,8 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
{
|
||||
if (a != null && a.ally_heroID == id)
|
||||
{
|
||||
// �ؼ�����ͬһ�� AllyHero_SO �������λʹ��ʱ������ֱ���� a.ally_currentEXP����������
|
||||
// ���Դ���Ҵ浵/PlayerPrefs ��ȡ������λ���ľ���ֵ��
|
||||
// �ؼ�����ͬһ�� AllyHero_SO �������λʹ��ʱ������ֱ����?a.ally_currentEXP����������
|
||||
// ���Դ���Ҵ�?PlayerPrefs ��ȡ������λ���ľ���ֵ��
|
||||
// Լ��������selected_heroSlot0{slotIndex+1}_exp������ selected_heroSlot01_exp����
|
||||
int expKeySlot = Mathf.Clamp(slotIndex + 1, 1, 5);
|
||||
string expKey = $"selected_heroSlot0{expKeySlot}_exp";
|
||||
@@ -344,7 +364,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
maxMana = eff.maxMana;
|
||||
damageResistance = eff.damageResistance;
|
||||
scoreEfficiency = eff.scoreEfficiency;
|
||||
attack = eff.attack;
|
||||
SetAttack(eff.attack);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -353,7 +373,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
maxMana = lvl.maxMana;
|
||||
damageResistance = lvl.damageResistance;
|
||||
scoreEfficiency = lvl.scoreEfficiency;
|
||||
attack = lvl.attack;
|
||||
SetAttack(lvl.attack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +385,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
|
||||
// ���ݴ��� EXP ����ȼ������� SO �ϵ� ally_currentEXP����������λ����״̬��
|
||||
// ���ݴ��� EXP ����ȼ�������?SO �ϵ� ally_currentEXP����������λ����״̬��
|
||||
private static AllyHero_SO.AllyLevelInfo GetEffectiveLevelForExp(AllyHero_SO so, int exp)
|
||||
{
|
||||
if (so == null || so.levelStats == null || so.levelStats.Count == 0) return null;
|
||||
@@ -417,6 +437,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
UpdateScoreUI();
|
||||
// update global total
|
||||
ScoreManager.Instance?.RecalculateTotal();
|
||||
EvaluateAttackZeroTriggers();
|
||||
return actuallyAdded;
|
||||
}
|
||||
|
||||
@@ -434,7 +455,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (currentScoreText != null)
|
||||
{
|
||||
currentScoreText.text = value;
|
||||
Debug.Log($"[AllyCombatant] Updated slot {slotIndex+1} score UI -> {value} (target {currentScoreText.gameObject.name})");
|
||||
LogVerbose($"[AllyCombatant] Updated slot {slotIndex+1} score UI -> {value} (target {currentScoreText.gameObject.name})");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -445,24 +466,33 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
switch (slotIndex)
|
||||
{
|
||||
case 0:
|
||||
if (ui.teammate01_current_scoreText != null) { ui.teammate01_current_scoreText.text = value; Debug.Log($"[AllyCombatant] Fallback wrote to ui.teammate01_current_scoreText: {value}"); }
|
||||
if (ui.teammate01_current_scoreText != null) { ui.teammate01_current_scoreText.text = value; LogVerbose($"[AllyCombatant] Fallback wrote to ui.teammate01_current_scoreText: {value}"); }
|
||||
break;
|
||||
case 1:
|
||||
if (ui.teammate02_current_scoreText != null) { ui.teammate02_current_scoreText.text = value; Debug.Log($"[AllyCombatant] Fallback wrote to ui.teammate02_current_scoreText: {value}"); }
|
||||
if (ui.teammate02_current_scoreText != null) { ui.teammate02_current_scoreText.text = value; LogVerbose($"[AllyCombatant] Fallback wrote to ui.teammate02_current_scoreText: {value}"); }
|
||||
break;
|
||||
case 2:
|
||||
if (ui.teammate03_current_scoreText != null) { ui.teammate03_current_scoreText.text = value; Debug.Log($"[AllyCombatant] Fallback wrote to ui.teammate03_current_scoreText: {value}"); }
|
||||
if (ui.teammate03_current_scoreText != null) { ui.teammate03_current_scoreText.text = value; LogVerbose($"[AllyCombatant] Fallback wrote to ui.teammate03_current_scoreText: {value}"); }
|
||||
break;
|
||||
case 3:
|
||||
if (ui.teammate04_current_scoreText != null) { ui.teammate04_current_scoreText.text = value; Debug.Log($"[AllyCombatant] Fallback wrote to ui.teammate04_current_scoreText: {value}"); }
|
||||
if (ui.teammate04_current_scoreText != null) { ui.teammate04_current_scoreText.text = value; LogVerbose($"[AllyCombatant] Fallback wrote to ui.teammate04_current_scoreText: {value}"); }
|
||||
break;
|
||||
case 4:
|
||||
if (ui.teammate05_current_scoreText != null) { ui.teammate05_current_scoreText.text = value; Debug.Log($"[AllyCombatant] Fallback wrote to ui.teammate05_current_scoreText: {value}"); }
|
||||
if (ui.teammate05_current_scoreText != null) { ui.teammate05_current_scoreText.text = value; LogVerbose($"[AllyCombatant] Fallback wrote to ui.teammate05_current_scoreText: {value}"); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set max score cap for this track and refresh UI
|
||||
public void SetMaxTrackScore(int newMax, bool clampCurrent = true)
|
||||
{
|
||||
maxTrackScore = Mathf.Max(0, newMax);
|
||||
if (clampCurrent)
|
||||
currentScore = Mathf.Clamp(currentScore, 0, maxTrackScore);
|
||||
UpdateScoreUI();
|
||||
}
|
||||
|
||||
// Public setters that update UI and optionally animate fade bars
|
||||
public void SetCurrentHP(int hp, bool animateFade = true)
|
||||
{
|
||||
@@ -562,6 +592,22 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
// EvaluateHPPercentageTriggers(currentHP); // Not needed for mana but keep in mind
|
||||
}
|
||||
|
||||
public void ModifyAttack(int delta)
|
||||
{
|
||||
int old = attack;
|
||||
attack = Mathf.Max(0, attack + delta);
|
||||
if (old != attack)
|
||||
EvaluateAttackZeroTriggers();
|
||||
}
|
||||
|
||||
public void SetAttack(int value)
|
||||
{
|
||||
int old = attack;
|
||||
attack = Mathf.Max(0, value);
|
||||
if (old != attack)
|
||||
EvaluateAttackZeroTriggers();
|
||||
}
|
||||
|
||||
private void UpdateHealthVisuals(int oldHP, bool animateFade)
|
||||
{
|
||||
// main bar should snap quickly to new value, while fade bar lerps towards it
|
||||
@@ -611,7 +657,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
var ui = teamUIController.Instance;
|
||||
if (ui != null)
|
||||
{
|
||||
// 移除原有的红闪常亮逻辑,改为调用黑白渐变效果
|
||||
// 移除原有的红闪常亮逻辑,改为调用黑白渐变效�?
|
||||
StartCoroutine(ui.AllyDeathGrayscale(characterImage));
|
||||
}
|
||||
}
|
||||
@@ -704,6 +750,23 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
|
||||
public void ReceiveDamage(float amount, GameObject source)
|
||||
{
|
||||
ReceiveDamageInternal(amount, source, true);
|
||||
}
|
||||
|
||||
private void ReceiveDamageInternal(float amount, GameObject source, bool allowRedirect)
|
||||
{
|
||||
if (allowRedirect && IsRedirectActive())
|
||||
{
|
||||
var redirector = s_nextDamageRedirector;
|
||||
ClearRedirect();
|
||||
if (redirector != null && redirector != this)
|
||||
{
|
||||
redirector.ReceiveDamageInternal(amount, source, false);
|
||||
return;
|
||||
}
|
||||
// If redirector is self or invalid, consume redirect and continue to take damage normally.
|
||||
}
|
||||
|
||||
float effective = amount * (1f - damageResistance);
|
||||
int delta = Mathf.CeilToInt(effective);
|
||||
ModifyHP(-delta, true);
|
||||
@@ -720,7 +783,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (buff == null) return;
|
||||
activeBuffs.Add(buff);
|
||||
// simple immediate apply: multiply stats
|
||||
if (buff.attackMultiplier != 1f) { attack = Mathf.RoundToInt(attack * buff.attackMultiplier); }
|
||||
if (buff.attackMultiplier != 1f) { SetAttack(Mathf.RoundToInt(attack * buff.attackMultiplier)); }
|
||||
if (buff.scoreMultiplier != 1f) scoreEfficiency *= buff.scoreMultiplier;
|
||||
// UI/visual could be added
|
||||
}
|
||||
@@ -847,7 +910,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (skill == null) continue;
|
||||
if (skill.triggerCondition != SkillDefinition.SkillTrigger.OnManaFull) continue;
|
||||
SkillBuilder.Instance.UseSkillDefinition(skill, slotIndex, -1f, null);
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast skill from group: {skill.skillId} (OnManaFull)");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} cast skill from group: {skill.skillId} (OnManaFull)");
|
||||
anyTriggered = true;
|
||||
}
|
||||
}
|
||||
@@ -864,7 +927,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (skill == null) continue;
|
||||
if (skill.triggerCondition != SkillDefinition.SkillTrigger.OnManaFull) continue;
|
||||
SkillBuilder.Instance.UseSkillDefinition(skill, slotIndex, -1f, null);
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast skill from primary group: {skill.skillId} (OnManaFull)");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} cast skill from primary group: {skill.skillId} (OnManaFull)");
|
||||
anyTriggered = true;
|
||||
}
|
||||
}
|
||||
@@ -878,7 +941,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (def != null && def.triggerCondition == SkillDefinition.SkillTrigger.OnManaFull)
|
||||
{
|
||||
SkillBuilder.Instance.UseSkillDefinition(def, slotIndex, -1f, null);
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast primary skill '{def.skillId}' due to ManaFull.");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} cast primary skill '{def.skillId}' due to ManaFull.");
|
||||
anyTriggered = true;
|
||||
}
|
||||
else if (so.availableSkills != null)
|
||||
@@ -888,7 +951,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (skill == null) continue;
|
||||
if (skill.triggerCondition != SkillDefinition.SkillTrigger.OnManaFull) continue;
|
||||
SkillBuilder.Instance.UseSkillDefinition(skill, slotIndex, -1f, null);
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast available skill: {skill.skillId} (OnManaFull)");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} cast available skill: {skill.skillId} (OnManaFull)");
|
||||
anyTriggered = true;
|
||||
}
|
||||
}
|
||||
@@ -896,7 +959,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
|
||||
if (!anyTriggered)
|
||||
{
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1}: no skills configured for OnManaFull.");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1}: no skills configured for OnManaFull.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,11 +974,11 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
// Only cast skills whose triggerCondition matches OnManaFull when this method is invoked from auto-cast path
|
||||
if (skill.triggerCondition != SkillDefinition.SkillTrigger.OnManaFull)
|
||||
{
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} skill '{skill.skillId}' skipped: triggerCondition={skill.triggerCondition}.");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} skill '{skill.skillId}' skipped: triggerCondition={skill.triggerCondition}.");
|
||||
continue;
|
||||
}
|
||||
SkillBuilder.Instance.UseSkillDefinition(skill, slotIndex, -1f, null);
|
||||
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast skill from group: {skill.skillId}");
|
||||
LogVerbose($"[AllyCombatant] Slot {slotIndex + 1} cast skill from group: {skill.skillId}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,7 +996,46 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
SetCurrentMana(maxMana, true);
|
||||
}
|
||||
|
||||
// New: Evaluate HP-percent triggers and handle enter/leave transitions
|
||||
|
||||
private Dictionary<string, float> BuildFormulaVars(AllyHero_SO so)
|
||||
{
|
||||
var vars = _formulaVarsBuffer;
|
||||
vars.Clear();
|
||||
vars["slot"] = slotIndex;
|
||||
vars["attack"] = attack;
|
||||
|
||||
if (so != null && so.levelStats != null && so.levelStats.Count > 0)
|
||||
{
|
||||
var eff = so.GetEffectiveLevelForCurrentEXP();
|
||||
var lvl = eff ?? so.levelStats[0];
|
||||
vars["maxHP"] = lvl.maxHP;
|
||||
vars["maxMana"] = lvl.maxMana;
|
||||
vars["damageResistance"] = lvl.damageResistance;
|
||||
vars["scoreEfficiency"] = lvl.scoreEfficiency;
|
||||
vars["attack"] = lvl.attack;
|
||||
vars["level"] = lvl.levelID;
|
||||
vars["levelID"] = lvl.levelID;
|
||||
vars["currentLevel"] = lvl.levelID;
|
||||
}
|
||||
else
|
||||
{
|
||||
vars["maxHP"] = maxHP;
|
||||
vars["maxMana"] = maxMana;
|
||||
vars["damageResistance"] = damageResistance;
|
||||
vars["scoreEfficiency"] = scoreEfficiency;
|
||||
vars["level"] = 0f;
|
||||
vars["levelID"] = 0f;
|
||||
vars["currentLevel"] = 0f;
|
||||
}
|
||||
|
||||
vars["ally_currentEXP"] = so != null ? so.ally_currentEXP : 0f;
|
||||
vars["currentMana"] = currentMana;
|
||||
vars["currentScore"] = currentScore;
|
||||
vars["idolScore"] = currentScore;
|
||||
vars["currentHP"] = currentHP;
|
||||
return vars;
|
||||
}
|
||||
// New: Evaluate HP-percent triggers and handle enter/leave transitions
|
||||
private void EvaluateHPPercentageTriggers(int oldHP)
|
||||
{
|
||||
if (!Application.isPlaying) return;
|
||||
@@ -945,6 +1047,77 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
float oldPct = (maxHP > 0) ? (float)oldHP / (float)maxHP : 0f;
|
||||
float currPct = (maxHP > 0) ? (float)currentHP / (float)maxHP : 0f;
|
||||
|
||||
// Collect candidate skills (same priority order as other triggers)
|
||||
var defs = _tmpSkillDefs;
|
||||
defs.Clear();
|
||||
if (so.equippedSkillGroupIDs != null && so.equippedSkillGroupIDs.Length > 0 && so.skillGroups != null)
|
||||
{
|
||||
foreach (var gid in so.equippedSkillGroupIDs)
|
||||
{
|
||||
if (gid == 0) continue;
|
||||
SkillGroup group = null;
|
||||
for (int k = 0; k < so.skillGroups.Length; k++)
|
||||
{
|
||||
var g = so.skillGroups[k];
|
||||
if (g != null && g.skillGroupID == gid) { group = g; break; }
|
||||
}
|
||||
if (group == null || group.skills == null) continue;
|
||||
for (int i = 0; i < group.skills.Length; i++)
|
||||
{
|
||||
var def = group.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fallbackGroup = so.GetPrimarySkillGroup();
|
||||
if (fallbackGroup != null && fallbackGroup.skills != null)
|
||||
{
|
||||
for (int i = 0; i < fallbackGroup.skills.Length; i++)
|
||||
{
|
||||
var def = fallbackGroup.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
else if (so.availableSkills != null)
|
||||
{
|
||||
for (int i = 0; i < so.availableSkills.Length; i++)
|
||||
{
|
||||
var def = so.availableSkills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Select a single "best" Above/Below skill to avoid stacking when multiple thresholds are satisfied.
|
||||
string selectedAbove = null;
|
||||
float selectedAboveThreshold = float.MinValue;
|
||||
string selectedBelow = null;
|
||||
float selectedBelowThreshold = float.MaxValue;
|
||||
for (int i = 0; i < defs.Count; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
if (def == null) continue;
|
||||
if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPAbovePercent)
|
||||
{
|
||||
if (currPct >= def.hpTriggerPercent && def.hpTriggerPercent >= selectedAboveThreshold)
|
||||
{
|
||||
selectedAboveThreshold = def.hpTriggerPercent;
|
||||
selectedAbove = def.skillId;
|
||||
}
|
||||
}
|
||||
else if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPBelowPercent)
|
||||
{
|
||||
if (currPct <= def.hpTriggerPercent && def.hpTriggerPercent <= selectedBelowThreshold)
|
||||
{
|
||||
selectedBelowThreshold = def.hpTriggerPercent;
|
||||
selectedBelow = def.skillId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vars = BuildFormulaVars(so);
|
||||
Action<SkillDefinition> processDef = (def) =>
|
||||
{
|
||||
if (def == null) return;
|
||||
@@ -954,33 +1127,12 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
bool wasActive = _activeHpPercentSkills.Contains(def.skillId);
|
||||
bool nowActive = false;
|
||||
if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPAbovePercent)
|
||||
nowActive = currPct >= def.hpTriggerPercent;
|
||||
nowActive = (selectedAbove != null && def.skillId == selectedAbove) && currPct >= def.hpTriggerPercent;
|
||||
else if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPBelowPercent)
|
||||
nowActive = currPct <= def.hpTriggerPercent;
|
||||
nowActive = (selectedBelow != null && def.skillId == selectedBelow) && currPct <= def.hpTriggerPercent;
|
||||
|
||||
// Evaluate formula into amount using similar variables as SkillBuilder
|
||||
float amount = 0f;
|
||||
var vars = new Dictionary<string, float>();
|
||||
vars["slot"] = slotIndex;
|
||||
vars["attack"] = attack;
|
||||
if (so != null && so.levelStats != null && so.levelStats.Count > 0)
|
||||
{
|
||||
var eff = so.GetEffectiveLevelForCurrentEXP();
|
||||
var lvl = eff ?? so.levelStats[0];
|
||||
vars["maxHP"] = lvl.maxHP;
|
||||
vars["maxMana"] = lvl.maxMana;
|
||||
vars["damageResistance"] = lvl.damageResistance;
|
||||
vars["scoreEfficiency"] = lvl.scoreEfficiency;
|
||||
vars["attack"] = lvl.attack;
|
||||
}
|
||||
else
|
||||
{
|
||||
vars["maxHP"] = maxHP;
|
||||
vars["maxMana"] = maxMana;
|
||||
vars["damageResistance"] = damageResistance;
|
||||
vars["scoreEfficiency"] = scoreEfficiency;
|
||||
}
|
||||
vars["ally_currentEXP"] = so != null ? so.ally_currentEXP : 0f;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(def.formula))
|
||||
{
|
||||
@@ -1028,7 +1180,22 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
};
|
||||
|
||||
// iterate equipped groups -> primary group -> availableSkills (same as other triggers)
|
||||
for (int i = 0; i < defs.Count; i++)
|
||||
processDef(defs[i]);
|
||||
}
|
||||
|
||||
private void EvaluateAttackZeroTriggers()
|
||||
{
|
||||
if (!Application.isPlaying) return;
|
||||
if (SkillBuilder.Instance == null) return;
|
||||
|
||||
var so = SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex);
|
||||
if (so == null) return;
|
||||
|
||||
bool nowActive = attack <= 0;
|
||||
|
||||
var defs = _tmpSkillDefs;
|
||||
defs.Clear();
|
||||
if (so.equippedSkillGroupIDs != null && so.equippedSkillGroupIDs.Length > 0 && so.skillGroups != null)
|
||||
{
|
||||
foreach (var gid in so.equippedSkillGroupIDs)
|
||||
@@ -1040,32 +1207,119 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
var g = so.skillGroups[k];
|
||||
if (g != null && g.skillGroupID == gid) { group = g; break; }
|
||||
}
|
||||
if (group == null) continue;
|
||||
foreach (var def in group.skills)
|
||||
if (group == null || group.skills == null) continue;
|
||||
for (int i = 0; i < group.skills.Length; i++)
|
||||
{
|
||||
processDef(def);
|
||||
var def = group.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var fallbackGroup = so.GetPrimarySkillGroup();
|
||||
if (fallbackGroup != null)
|
||||
else
|
||||
{
|
||||
foreach (var def in fallbackGroup.skills)
|
||||
var fallbackGroup = so.GetPrimarySkillGroup();
|
||||
if (fallbackGroup != null && fallbackGroup.skills != null)
|
||||
{
|
||||
processDef(def);
|
||||
for (int i = 0; i < fallbackGroup.skills.Length; i++)
|
||||
{
|
||||
var def = fallbackGroup.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (so.availableSkills != null)
|
||||
{
|
||||
foreach (var def in so.availableSkills)
|
||||
else if (so.availableSkills != null)
|
||||
{
|
||||
processDef(def);
|
||||
for (int i = 0; i < so.availableSkills.Length; i++)
|
||||
{
|
||||
var def = so.availableSkills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vars = BuildFormulaVars(so);
|
||||
|
||||
for (int i = 0; i < defs.Count; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
if (def == null) continue;
|
||||
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnAttackZero) continue;
|
||||
|
||||
bool wasActive = _activeAttackZeroSkills.Contains(def.skillId);
|
||||
|
||||
// Evaluate formula into amount using similar variables as SkillBuilder
|
||||
float amount = 0f;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(def.formula))
|
||||
{
|
||||
if (!SkillDefinition.TryEvaluateFormula(def.formula, vars, out float fresult))
|
||||
amount = 0f;
|
||||
else
|
||||
amount = fresult;
|
||||
}
|
||||
|
||||
bool isReversible = IsEffectReversible(def.effectType);
|
||||
if (!isReversible)
|
||||
{
|
||||
if (!wasActive && nowActive)
|
||||
{
|
||||
_activeAttackZeroSkills.Add(def.skillId);
|
||||
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null);
|
||||
}
|
||||
else if (wasActive && !nowActive)
|
||||
{
|
||||
_activeAttackZeroSkills.Remove(def.skillId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!wasActive && nowActive)
|
||||
{
|
||||
_activeAttackZeroSkills.Add(def.skillId);
|
||||
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null);
|
||||
_appliedAttackZeroEffects[def.skillId] = new AppliedEffect(def.effectType, amount);
|
||||
}
|
||||
else if (wasActive && !nowActive)
|
||||
{
|
||||
_activeAttackZeroSkills.Remove(def.skillId);
|
||||
if (_appliedAttackZeroEffects.TryGetValue(def.skillId, out var applied))
|
||||
{
|
||||
ApplyInverseEffect(def.defaultSelector, applied.effectType, applied.amount);
|
||||
_appliedAttackZeroEffects.Remove(def.skillId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ActivateNextDamageRedirect(AllyCombatant redirector, float duration = 0f)
|
||||
{
|
||||
if (redirector == null) return;
|
||||
s_nextDamageRedirector = redirector;
|
||||
if (duration > 0f && Application.isPlaying)
|
||||
s_redirectExpireTime = Time.time + duration;
|
||||
else
|
||||
s_redirectExpireTime = -1f;
|
||||
}
|
||||
|
||||
private static bool IsRedirectActive()
|
||||
{
|
||||
if (s_nextDamageRedirector == null) return false;
|
||||
if (Application.isPlaying && s_redirectExpireTime > 0f && Time.time > s_redirectExpireTime)
|
||||
{
|
||||
ClearRedirect();
|
||||
return false;
|
||||
}
|
||||
if (s_nextDamageRedirector != null && s_nextDamageRedirector.isDead)
|
||||
{
|
||||
ClearRedirect();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ClearRedirect()
|
||||
{
|
||||
s_nextDamageRedirector = null;
|
||||
s_redirectExpireTime = -1f;
|
||||
}
|
||||
|
||||
private bool IsEffectReversible(EffectType type)
|
||||
@@ -1140,3 +1394,17 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user