成就系统 巨大修改 新的ui 黑白效果 等一堆

This commit is contained in:
2026-01-23 18:43:23 +08:00
parent f3ece026ca
commit 0e47864569
256 changed files with 134131 additions and 31192 deletions
+498 -66
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -6,15 +7,15 @@ using TMPro;
[ExecuteAlways]
/// <summary>
/// AllyCombatant 绑定到场上友军的运行时 GameObject(命名为 ally_01..ally_05),
/// 实现 ICombatant 接口以供 EffectSystem 调用。
/// - teamUIController 获取 UI 引用并同步显示血量/法力
/// - 启动时从 TeamCharacterDataInfo AllyHero_SO 填充最大生命/法力等基础属性
/// - 初始生命值为最大生命值,初始法力为 0
/// AllyCombatant 󶨵Ѿʱ GameObjectΪ ally_01..ally_05
/// ʵ ICombatant ӿԹ EffectSystem á
/// - teamUIController ȡ UI òͬʾѪ/
/// - ʱ TeamCharacterDataInfo AllyHero_SO /Ȼ
/// - ʼֵΪֵʼΪ 0
/// </summary>
public class AllyCombatant : MonoBehaviour, ICombatant
{
public int slotIndex = 0; // 0..4 对应 ally_01..ally_05
public int slotIndex = 0; // 0..4 Ӧ ally_01..ally_05
// runtime stats
public int maxHP = 100;
@@ -28,7 +29,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
public BeatmapManager bmm;
// 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
@@ -46,10 +47,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.
@@ -71,10 +72,25 @@ public class AllyCombatant : MonoBehaviour, ICombatant
private Coroutine fadeManaCoroutine;
private List<Buff> activeBuffs = new List<Buff>();
// Track skills that are currently active because HP percent condition is satisfied.
private HashSet<string> _activeHpPercentSkills = new HashSet<string>();
// Track applied reversible effects for HP-percent skills so we can revert them on leave
private Dictionary<string, AppliedEffect> _appliedHpPercentEffects = new Dictionary<string, AppliedEffect>();
private class AppliedEffect
{
public EffectType effectType;
public float amount;
public AppliedEffect(EffectType t, float a) { effectType = t; amount = a; }
}
private void Awake()
{
// ensure GameObject name matches pattern so EffectSystem can find it
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>();
}
private void Start()
@@ -84,13 +100,52 @@ public class AllyCombatant : MonoBehaviour, ICombatant
if (allowOverwriteFromSO)
InitializeStatsFromData();
// Ensure runtime invariants
currentHP = Mathf.Clamp(currentHP, 0, Mathf.Max(1, maxHP));
currentMana = Mathf.Clamp(currentMana, 0, Mathf.Max(1, maxMana));
// ؼ޸볡ʱӦѪ/0
// ֮ǰֻ Clamp prefab/ inspector ϵľ currentHPѪ
isDead = false;
currentHP = Mathf.Max(1, maxHP);
currentMana = 0;
UpdateUIImmediate();
// Ensure score UI shows initial value
UpdateScoreUI();
// Evaluate HP-percent triggers on start after a short delay to ensure singletons are ready
if (Application.isPlaying)
{
StartCoroutine(InitialTriggerCheckCoroutine());
}
}
private IEnumerator InitialTriggerCheckCoroutine()
{
// Wait for singletons to be ready
while (SkillBuilder.Instance == null || teamUIController.Instance == null)
{
yield return null;
}
// Wait until teamUIController has finished loading ally SOs if they are async
// or just wait a couple of frames to be sure
yield return null;
yield return null;
// Re-run initialization to ensure we have the correct MaxHP and skills from the loaded team
if (allowOverwriteFromSO)
{
InitializeStatsFromData();
currentHP = Mathf.Max(1, maxHP);
UpdateUIImmediate();
}
// Final wait to ensure SkillBuilder's internal mapping is ready
while (SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex) == null)
{
yield return null;
}
// Perform the initial HP-percentage trigger check (oldHP = currentHP to trigger "ENTER" if condition met)
EvaluateHPPercentageTriggers(currentHP);
}
// Allow editing in inspector to immediately reflect on UI (editor only)
@@ -107,10 +162,10 @@ public class AllyCombatant : MonoBehaviour, ICombatant
// Do not start coroutines from OnValidate - update immediately
UpdateUIImmediate();
// If user adjusts currentMana in inspector while in Play mode, attempt to trigger mana-full behaviors
// Guard to avoid calling TryCastOnFullMana before singletons are initialized
// If user adjusts currentHP or currentMana in inspector while in Play mode, attempt to trigger behaviors
if (Application.isPlaying && SkillBuilder.Instance != null)
{
EvaluateHPPercentageTriggers(currentHP);
TryCastOnFullMana();
}
}
@@ -270,17 +325,26 @@ public class AllyCombatant : MonoBehaviour, ICombatant
{
if (a != null && a.ally_heroID == id)
{
// pick effective level based on current EXP if available
// ؼ޸ͬһ 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";
int slotExp = a.ally_currentEXP;
if (PlayerPrefs.HasKey(expKey))
slotExp = PlayerPrefs.GetInt(expKey, a.ally_currentEXP);
// pick effective level based on per-slot EXP
if (a.levelStats != null && a.levelStats.Count > 0)
{
var eff = a.GetEffectiveLevelForCurrentEXP();
var eff = GetEffectiveLevelForExp(a, slotExp);
if (eff != null)
{
maxHP = eff.maxHP;
maxMana = eff.maxMana;
damageResistance = eff.damageResistance;
scoreEfficiency = eff.scoreEfficiency;
attack = eff.attack; // populate attack from SO effective level
attack = eff.attack;
}
else
{
@@ -301,6 +365,23 @@ public class AllyCombatant : MonoBehaviour, ICombatant
}
}
// ݴ 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;
AllyHero_SO.AllyLevelInfo best = null;
foreach (var lvl in so.levelStats)
{
if (lvl == null) continue;
if (exp >= lvl.requiredEXP)
{
if (best == null || lvl.requiredEXP >= best.requiredEXP)
best = lvl;
}
}
return best ?? so.levelStats[0];
}
// Scoring API
// Modified to return the amount added so callers can track per-track contributions
public int AddScoreForJudge(string judge)
@@ -390,6 +471,20 @@ public class AllyCombatant : MonoBehaviour, ICombatant
currentHP = Mathf.Clamp(hp, 0, maxHP);
if (currentHP <= 0) isDead = true;
UpdateHealthVisuals(old, animateFade);
// Trigger HP changed events
int delta = currentHP - old;
if (delta > 0)
{
TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger.OnHPHealed);
}
else if (delta < 0)
{
TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger.OnHPLost);
}
// Evaluate HP-percentage based trigger transitions (entry/exit)
EvaluateHPPercentageTriggers(old);
}
public void ModifyHP(int delta, bool animateFade = true)
@@ -405,44 +500,66 @@ public class AllyCombatant : MonoBehaviour, ICombatant
public void SetMaxHP(int newMax, bool keepCurrentRatio = true)
{
if (newMax < 1) newMax = 1;
int oldMax = maxHP;
int oldHP = currentHP;
float ratio = maxHP > 0 ? (float)currentHP / maxHP : 1f;
maxHP = newMax;
if (keepCurrentRatio)
currentHP = Mathf.Clamp(Mathf.RoundToInt(ratio * maxHP), 0, maxHP);
else
currentHP = Mathf.Clamp(currentHP, 0, maxHP);
UpdateUIImmediate();
// Max HP change affects percentage triggers
EvaluateHPPercentageTriggers(oldHP);
}
public void SetCurrentMana(int mana, bool animateFade = true)
public void SetCurrentMana(int mana, bool animateFade = true, bool flashVisuals = true)
{
if (isDead) return; // dead characters cannot change mana
int old = currentMana;
currentMana = Mathf.Clamp(mana, 0, maxMana);
UpdateManaVisuals(old, animateFade);
UpdateManaVisuals(old, animateFade, flashVisuals);
TryCastOnFullMana();
// Trigger mana changed events
int delta = currentMana - old;
if (delta > 0)
{
TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger.OnManaGained);
}
else if (delta < 0)
{
TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger.OnManaLost);
}
}
public void ModifyMana(int delta, bool animateFade = true)
public void ModifyMana(int delta, bool animateFade = true, bool flashVisuals = true)
{
if (isDead && delta > 0)
{
// dead units cannot gain mana
return;
}
SetCurrentMana(currentMana + delta, animateFade);
SetCurrentMana(currentMana + delta, animateFade, flashVisuals);
}
public void SetMaxMana(int newMax, bool keepCurrentRatio = true)
{
if (newMax < 1) newMax = 1;
int oldMax = maxMana;
int oldMana = currentMana;
float ratio = maxMana > 0 ? (float)currentMana / maxMana : 1f;
maxMana = newMax;
if (keepCurrentRatio)
currentMana = Mathf.Clamp(Mathf.RoundToInt(ratio * maxMana), 0, maxMana);
else
currentMana = Mathf.Clamp(currentMana, 0, maxMana);
UpdateUIImmediate();
// Mana doesn't currently have percentage triggers, but for consistency:
// EvaluateHPPercentageTriggers(currentHP); // Not needed for mana but keep in mind
}
private void UpdateHealthVisuals(int oldHP, bool animateFade)
@@ -468,21 +585,39 @@ public class AllyCombatant : MonoBehaviour, ICombatant
fadeHealthImage.fillAmount = target;
}
// if HP decreased, show hurt flash; if dead, keep hurt overlay visible
if (currentHP < oldHP && hurtRedImage != null && Application.isPlaying)
// HP change flash effects
if (hurtRedImage != null && Application.isPlaying)
{
StopCoroutine("HurtFlash");
StartCoroutine(HurtFlash());
var ui = teamUIController.Instance;
if (ui != null)
{
if (currentHP < oldHP)
{
// Damage flash (Red)
if (!isDead)
StartCoroutine(ui.AllyImageFlash(hurtRedImage, ui.hurtColor));
}
else if (currentHP > oldHP)
{
// Heal flash (Green)
StartCoroutine(ui.AllyImageFlash(hurtRedImage, ui.healColor));
}
}
}
if (isDead && hurtRedImage != null)
// if dead, trigger grayscale effect on character image
if (isDead && characterImage != null && Application.isPlaying)
{
Color c = hurtRedImage.color;
c.a = 1f;
hurtRedImage.color = c; // keep overlay visible when dead
var ui = teamUIController.Instance;
if (ui != null)
{
// 移除原有的红闪常亮逻辑,改为调用黑白渐变效果
StartCoroutine(ui.AllyDeathGrayscale(characterImage));
}
}
}
private void UpdateManaVisuals(int oldMana, bool animateFade)
private void UpdateManaVisuals(int oldMana, bool animateFade, bool flashVisuals = true)
{
// main bar snaps, fade bar lerps to new value for visual effect
if (manaImage != null)
@@ -500,6 +635,16 @@ public class AllyCombatant : MonoBehaviour, ICombatant
else
fadeManaImage.fillAmount = target;
}
// Mana recovery flash effect
if (flashVisuals && currentMana > oldMana && hurtRedImage != null && Application.isPlaying)
{
var ui = teamUIController.Instance;
if (ui != null)
{
StartCoroutine(ui.AllyImageFlash(hurtRedImage, ui.manaColor));
}
}
}
private void UpdateUIImmediate()
@@ -564,31 +709,6 @@ public class AllyCombatant : MonoBehaviour, ICombatant
ModifyHP(-delta, true);
}
private IEnumerator HurtFlash()
{
if (hurtRedImage == null) yield break;
Color c = hurtRedImage.color;
c.a = 1f;
hurtRedImage.color = c;
// when injured, remain visible a bit longer; if dead, keep it on
if (isDead)
{
yield break;
}
yield return new WaitForSeconds(0.2f);
float dur = 0.4f;
float t = 0f;
while (t < dur)
{
t += Time.deltaTime;
c.a = Mathf.Lerp(1f, 0f, t / dur);
hurtRedImage.color = c;
yield return null;
}
c.a = 0f;
hurtRedImage.color = c;
}
public void ReceiveHeal(float amount, GameObject source)
{
int delta = Mathf.CeilToInt(amount);
@@ -616,6 +736,62 @@ public class AllyCombatant : MonoBehaviour, ICombatant
}
}
private void TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger when)
{
if (!Application.isPlaying) return;
if (SkillBuilder.Instance == null) return;
var so = SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex);
if (so == null) return;
// If equipped groups exist, iterate them first
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) continue;
foreach (var def in group.skills)
{
if (def == null) continue;
if (def.triggerCondition != when) continue;
SkillBuilder.Instance.UseSkillDefinition(def, slotIndex, -1f, null);
}
}
return;
}
// Fallback: check primary group
var fallbackGroup = so.GetPrimarySkillGroup();
if (fallbackGroup != null)
{
foreach (var def in fallbackGroup.skills)
{
if (def == null) continue;
if (def.triggerCondition != when) continue;
SkillBuilder.Instance.UseSkillDefinition(def, slotIndex, -1f, null);
}
return;
}
// Final fallback: iterate availableSkills
if (so.availableSkills != null)
{
foreach (var def in so.availableSkills)
{
if (def == null) continue;
if (def.triggerCondition != when) continue;
SkillBuilder.Instance.UseSkillDefinition(def, slotIndex, -1f, null);
}
}
}
private void TryCastOnFullMana()
{
if (currentMana >= maxMana && Application.isPlaying)
@@ -655,23 +831,72 @@ public class AllyCombatant : MonoBehaviour, ICombatant
return;
}
var def = so.GetPrimarySkill();
if (def == null)
bool anyTriggered = false;
// First: equipped groups
if (so.equippedSkillGroupIDs != null && so.equippedSkillGroupIDs.Length > 0 && so.skillGroups != null)
{
Debug.LogWarning($"[AllyCombatant] Slot {slotIndex + 1} cannot cast: primary skill not set in SO.");
return;
foreach (int 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) continue;
foreach (var skill in group.skills)
{
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)");
anyTriggered = true;
}
}
}
// Only auto-cast here if the skill's trigger condition matches ManaFull
if (def.triggerCondition == SkillDefinition.SkillTrigger.OnManaFull)
// Second: primary group
if (!anyTriggered)
{
// Use the SkillDefinition API to ensure selector/resolution/operateDirectly are respected
SkillBuilder.Instance.UseSkillDefinition(def, slotIndex, -1f, null);
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} cast primary skill '{def.skillId}' due to ManaFull.");
var fallbackGroup = so.GetPrimarySkillGroup();
if (fallbackGroup != null)
{
foreach (var skill in fallbackGroup.skills)
{
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)");
anyTriggered = true;
}
}
}
else
// Third: availableSkills / primary skill fallback
if (!anyTriggered)
{
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1} primary skill '{def.skillId}' not set to trigger on ManaFull (trigger={def.triggerCondition}); skipping cast.");
// try primary skill specifically
var def = so.GetPrimarySkill();
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.");
anyTriggered = true;
}
else if (so.availableSkills != null)
{
foreach (var skill in so.availableSkills)
{
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)");
anyTriggered = true;
}
}
}
if (!anyTriggered)
{
Debug.Log($"[AllyCombatant] Slot {slotIndex + 1}: no skills configured for OnManaFull.");
}
}
@@ -707,4 +932,211 @@ public class AllyCombatant : MonoBehaviour, ICombatant
{
SetCurrentMana(maxMana, true);
}
// New: Evaluate HP-percent triggers and handle enter/leave transitions
private void EvaluateHPPercentageTriggers(int oldHP)
{
if (!Application.isPlaying) return;
if (SkillBuilder.Instance == null) return;
var so = SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex);
if (so == null) return;
float oldPct = (maxHP > 0) ? (float)oldHP / (float)maxHP : 0f;
float currPct = (maxHP > 0) ? (float)currentHP / (float)maxHP : 0f;
Action<SkillDefinition> processDef = (def) =>
{
if (def == null) return;
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnHPAbovePercent && def.triggerCondition != SkillDefinition.SkillTrigger.OnHPBelowPercent)
return;
bool wasActive = _activeHpPercentSkills.Contains(def.skillId);
bool nowActive = false;
if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPAbovePercent)
nowActive = currPct >= def.hpTriggerPercent;
else if (def.triggerCondition == SkillDefinition.SkillTrigger.OnHPBelowPercent)
nowActive = 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))
{
if (!SkillDefinition.TryEvaluateFormula(def.formula, vars, out float fresult))
amount = 0f;
else
amount = fresult;
}
// Decide if this effect type is reversible (state-based)
bool isReversible = IsEffectReversible(def.effectType);
if (!isReversible)
{
// For non-reversible effects (like Damage or instant Heal), we don't track state
// Just trigger it once when the condition is first met
if (!wasActive && nowActive)
{
_activeHpPercentSkills.Add(def.skillId);
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null);
}
else if (wasActive && !nowActive)
{
_activeHpPercentSkills.Remove(def.skillId);
}
return;
}
if (!wasActive && nowActive)
{
// ENTER condition: Apply effect and record amount
_activeHpPercentSkills.Add(def.skillId);
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null);
_appliedHpPercentEffects[def.skillId] = new AppliedEffect(def.effectType, amount);
}
else if (wasActive && !nowActive)
{
// LEAVE condition: Revert effect using recorded amount
_activeHpPercentSkills.Remove(def.skillId);
if (_appliedHpPercentEffects.TryGetValue(def.skillId, out var applied))
{
ApplyInverseEffect(def.defaultSelector, applied.effectType, applied.amount);
_appliedHpPercentEffects.Remove(def.skillId);
}
}
};
// iterate equipped groups -> primary group -> availableSkills (same as other triggers)
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) continue;
foreach (var def in group.skills)
{
processDef(def);
}
}
return;
}
var fallbackGroup = so.GetPrimarySkillGroup();
if (fallbackGroup != null)
{
foreach (var def in fallbackGroup.skills)
{
processDef(def);
}
return;
}
if (so.availableSkills != null)
{
foreach (var def in so.availableSkills)
{
processDef(def);
}
}
}
private bool IsEffectReversible(EffectType type)
{
switch (type)
{
case EffectType.IncreaseAttack:
case EffectType.DecreaseAttack:
case EffectType.IncreaseMaxHP:
case EffectType.DecreaseMaxHP:
case EffectType.IncreaseMaxMana:
case EffectType.DecreaseMaxMana:
case EffectType.IncreaseScoreEfficiency:
case EffectType.DecreaseScoreEfficiency:
case EffectType.IncreaseDamageResistance:
case EffectType.DecreaseDamageResistance:
case EffectType.ScoreMultiplier:
case EffectType.AddScore:
return true;
default:
return false;
}
}
private void ApplyInverseEffect(Selector selector, EffectType type, float amount)
{
if (EffectSystem.Instance == null) return;
switch (type)
{
case EffectType.IncreaseAttack:
EffectSystem.Instance.ApplyEffect(selector, EffectType.DecreaseAttack, amount, 0f, this.gameObject, null);
break;
case EffectType.DecreaseAttack:
EffectSystem.Instance.ApplyEffect(selector, EffectType.IncreaseAttack, amount, 0f, this.gameObject, null);
break;
case EffectType.IncreaseMaxHP:
EffectSystem.Instance.ApplyEffect(selector, EffectType.DecreaseMaxHP, amount, 0f, this.gameObject, null);
break;
case EffectType.DecreaseMaxHP:
EffectSystem.Instance.ApplyEffect(selector, EffectType.IncreaseMaxHP, amount, 0f, this.gameObject, null);
break;
case EffectType.IncreaseMaxMana:
EffectSystem.Instance.ApplyEffect(selector, EffectType.DecreaseMaxMana, amount, 0f, this.gameObject, null);
break;
case EffectType.DecreaseMaxMana:
EffectSystem.Instance.ApplyEffect(selector, EffectType.IncreaseMaxMana, amount, 0f, this.gameObject, null);
break;
case EffectType.IncreaseScoreEfficiency:
EffectSystem.Instance.ApplyEffect(selector, EffectType.DecreaseScoreEfficiency, amount, 0f, this.gameObject, null);
break;
case EffectType.DecreaseScoreEfficiency:
EffectSystem.Instance.ApplyEffect(selector, EffectType.IncreaseScoreEfficiency, amount, 0f, this.gameObject, null);
break;
case EffectType.IncreaseDamageResistance:
EffectSystem.Instance.ApplyEffect(selector, EffectType.DecreaseDamageResistance, amount, 0f, this.gameObject, null);
break;
case EffectType.DecreaseDamageResistance:
EffectSystem.Instance.ApplyEffect(selector, EffectType.IncreaseDamageResistance, amount, 0f, this.gameObject, null);
break;
case EffectType.ScoreMultiplier:
if (Mathf.Abs(amount) > 0.0001f)
EffectSystem.Instance.ApplyEffect(selector, EffectType.ScoreMultiplier, 1f / amount, 0f, this.gameObject, null);
break;
case EffectType.AddScore:
EffectSystem.Instance.ApplyEffect(selector, EffectType.AddScore, -amount, 0f, this.gameObject, null);
break;
default:
// Fallback: apply same type with negative amount if it's likely a numeric stat
EffectSystem.Instance.ApplyEffect(selector, type, -amount, 0f, this.gameObject, null);
break;
}
}
}