gameplay所有基本功能都装了,加入了全局警告。加入飘字等各种东西。
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@@ -32,9 +32,12 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
// --- Damage redirect (next hit) ---
|
||||
private static AllyCombatant s_nextDamageRedirector;
|
||||
private static float s_redirectExpireTime = -1f;
|
||||
private static string s_redirectIconId;
|
||||
private static int s_redirectIconSlotIndex = -1;
|
||||
|
||||
// --- Self damage redirect to adjacent (duration) ---
|
||||
private float _selfDamageRedirectToAdjacentExpireTime = -1f;
|
||||
private string _selfDamageRedirectToAdjacentIconId;
|
||||
|
||||
// add attack stat to be available at runtime
|
||||
[Tooltip("Documentation text normalized.")]
|
||||
@@ -76,6 +79,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
private Image manaImage;
|
||||
private Image fadeManaImage;
|
||||
private TextMeshProUGUI nameText;
|
||||
public string allyName => nameText != null ? nameText.text : name;
|
||||
private TextMeshProUGUI healthRateText;
|
||||
private TextMeshProUGUI currentScoreText;
|
||||
private Image hurtRedImage;
|
||||
@@ -177,6 +181,21 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
if (s_redirectExpireTime > 0f && Time.time > s_redirectExpireTime) ClearRedirect();
|
||||
|
||||
if (_selfDamageRedirectToAdjacentExpireTime > 0f && Time.time > _selfDamageRedirectToAdjacentExpireTime)
|
||||
{
|
||||
_selfDamageRedirectToAdjacentExpireTime = -1f;
|
||||
if (!string.IsNullOrEmpty(_selfDamageRedirectToAdjacentIconId))
|
||||
{
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(slotIndex, _selfDamageRedirectToAdjacentIconId);
|
||||
_selfDamageRedirectToAdjacentIconId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -196,6 +215,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
UpdateUIImmediate();
|
||||
// Ensure score UI shows initial value
|
||||
UpdateScoreUI();
|
||||
iBudeffPrefabController.Instance?.RegisterBaseline(this);
|
||||
|
||||
// Evaluate HP-percent triggers on start after a short delay to ensure singletons are ready
|
||||
if (Application.isPlaying)
|
||||
@@ -224,6 +244,8 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
currentHP = Mathf.Max(1, maxHP);
|
||||
UpdateUIImmediate();
|
||||
}
|
||||
|
||||
iBudeffPrefabController.Instance?.RegisterBaseline(this);
|
||||
|
||||
// Final wait to ensure SkillBuilder's internal mapping is ready
|
||||
while (SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex) == null)
|
||||
@@ -1071,6 +1093,14 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (old > 0 && currentHP <= 0)
|
||||
{
|
||||
TryTriggerSkillsOnEvent(SkillDefinition.SkillTrigger.OnSelfDefeated);
|
||||
string dName = "Unknown";
|
||||
if (nameText != null) dName = nameText.text;
|
||||
else if (SkillBuilder.Instance != null)
|
||||
{
|
||||
var so = SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex);
|
||||
if (so != null) dName = so.ally_heroName;
|
||||
}
|
||||
SkillTriggerFeedUI.PushDeath(dName);
|
||||
}
|
||||
|
||||
// Evaluate HP-percentage based trigger transitions (entry/exit)
|
||||
@@ -1236,6 +1266,11 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
return mult;
|
||||
}
|
||||
|
||||
public float GetHealReceivedMultiplierForUI()
|
||||
{
|
||||
return GetTotalHealReceivedMultiplier();
|
||||
}
|
||||
|
||||
private void RecalculateAttackFromBuffs()
|
||||
{
|
||||
EnsureAttackBaseInitialized();
|
||||
@@ -1460,7 +1495,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
// Apply reversible multipliers. attackMultiplier is applied via baseline+recalc to ensure it can be reverted.
|
||||
if (buff.scoreMultiplier != 1f) scoreEfficiency *= buff.scoreMultiplier;
|
||||
if (buff.attackMultiplier != 1f) RecalculateAttackFromBuffs();
|
||||
// UI/visual could be added
|
||||
iBudeffPrefabController.Instance?.NotifyBuffApplied(this, buff);
|
||||
}
|
||||
|
||||
public void RemoveBuff(string buffId)
|
||||
@@ -1471,6 +1506,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
activeBuffs.Remove(b);
|
||||
if (b.scoreMultiplier != 1f) scoreEfficiency /= b.scoreMultiplier;
|
||||
if (b.attackMultiplier != 1f) RecalculateAttackFromBuffs();
|
||||
iBudeffPrefabController.Instance?.NotifyBuffRemoved(this, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2126,11 +2162,23 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
public static void ActivateNextDamageRedirect(AllyCombatant redirector, float duration = 0f)
|
||||
{
|
||||
if (redirector == null) return;
|
||||
if (!string.IsNullOrEmpty(s_redirectIconId) && s_redirectIconSlotIndex >= 0)
|
||||
{
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(s_redirectIconSlotIndex, s_redirectIconId);
|
||||
s_redirectIconId = null;
|
||||
s_redirectIconSlotIndex = -1;
|
||||
}
|
||||
s_nextDamageRedirector = redirector;
|
||||
if (duration > 0f && Application.isPlaying)
|
||||
s_redirectExpireTime = Time.time + duration;
|
||||
else
|
||||
s_redirectExpireTime = -1f;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
s_redirectIconSlotIndex = redirector.slotIndex;
|
||||
s_redirectIconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(redirector, PlayerBudeffIconType.dmg_redirect_toSelf, 0f, duration);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRedirectActive()
|
||||
@@ -2151,6 +2199,12 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
|
||||
private static void ClearRedirect()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(s_redirectIconId) && s_redirectIconSlotIndex >= 0)
|
||||
{
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(s_redirectIconSlotIndex, s_redirectIconId);
|
||||
s_redirectIconId = null;
|
||||
s_redirectIconSlotIndex = -1;
|
||||
}
|
||||
s_nextDamageRedirector = null;
|
||||
s_redirectExpireTime = -1f;
|
||||
}
|
||||
@@ -2160,6 +2214,12 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (!Application.isPlaying) return;
|
||||
if (duration <= 0f) duration = 0.01f;
|
||||
_selfDamageRedirectToAdjacentExpireTime = Time.time + duration;
|
||||
if (!string.IsNullOrEmpty(_selfDamageRedirectToAdjacentIconId))
|
||||
{
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(slotIndex, _selfDamageRedirectToAdjacentIconId);
|
||||
_selfDamageRedirectToAdjacentIconId = null;
|
||||
}
|
||||
_selfDamageRedirectToAdjacentIconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(this, PlayerBudeffIconType.dmg_redirect_toAdjacent, 0f, duration);
|
||||
}
|
||||
|
||||
private bool IsSelfDamageRedirectToAdjacentActive()
|
||||
@@ -2297,5 +2357,3 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ public class EffectSystem : MonoBehaviour
|
||||
private int _cachedEnemiesFrame = -1;
|
||||
private readonly List<GameObject> _cachedEnemies = new List<GameObject>(8);
|
||||
|
||||
private readonly HashSet<int> _awaitingHitFxTargets = new HashSet<int>();
|
||||
private readonly Dictionary<int, int> _pendingDamagePopupByTargetId = new Dictionary<int, int>(64);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null) Instance = this;
|
||||
@@ -101,12 +104,15 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
GfxController.Instance.PlayHitFX(targetToHit, null, isEnemyTarget, finalPos, null, damageInfo);
|
||||
}
|
||||
FlushPendingDamagePopupAfterHit(targetToHit);
|
||||
});
|
||||
if (targetToHit != null) MarkAwaitingHitFx(targetToHit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (effectType)
|
||||
{
|
||||
case EffectType.DamageSingleEnemy:
|
||||
@@ -117,7 +123,24 @@ public class EffectSystem : MonoBehaviour
|
||||
|
||||
case EffectType.DamageOverTimeEnemy:
|
||||
foreach (var t in targets)
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source));
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source, null, -1, 0));
|
||||
continue;
|
||||
}
|
||||
var enemyDot = t.GetComponent<EnemyCombatant>() ?? t.GetComponentInChildren<EnemyCombatant>(true);
|
||||
string iconId = null;
|
||||
int enemyInstanceId = 0;
|
||||
if (enemyDot != null && duration > 0f)
|
||||
{
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = amount / ticks;
|
||||
iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyDot, PlayerBudeffIconType.ot_bleeding, perTick, duration);
|
||||
enemyInstanceId = enemyDot.GetInstanceID();
|
||||
}
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source, iconId, -1, enemyInstanceId));
|
||||
}
|
||||
break;
|
||||
|
||||
case EffectType.HealSingleEnemy:
|
||||
@@ -146,7 +169,22 @@ public class EffectSystem : MonoBehaviour
|
||||
|
||||
case EffectType.DamageOverTimeAlly:
|
||||
foreach (var t in targets)
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source));
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source, null, -1, 0));
|
||||
continue;
|
||||
}
|
||||
var allyDot = t.GetComponent<AllyCombatant>() ?? t.GetComponentInChildren<AllyCombatant>(true);
|
||||
string iconId = null;
|
||||
if (allyDot != null && duration > 0f)
|
||||
{
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = amount / ticks;
|
||||
iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(allyDot, PlayerBudeffIconType.ot_bleeding, perTick, duration);
|
||||
}
|
||||
StartCoroutine(ApplyDamageOverTimeCoroutine(t, amount, duration, tickInterval, source, iconId, allyDot != null ? allyDot.slotIndex : -1, 0));
|
||||
}
|
||||
break;
|
||||
|
||||
case EffectType.HealSingleSelf:
|
||||
@@ -228,7 +266,14 @@ public class EffectSystem : MonoBehaviour
|
||||
var ally = t.GetComponent<AllyCombatant>();
|
||||
if (ally != null)
|
||||
{
|
||||
int before = ally.currentScore;
|
||||
ally.AddScoreDirect(Mathf.CeilToInt(amount));
|
||||
int delta = ally.currentScore - before;
|
||||
if (delta != 0)
|
||||
{
|
||||
var type = delta > 0 ? iNumberPrefabController.InstantNumberType.IscorePlus : iNumberPrefabController.InstantNumberType.IscoreMinus;
|
||||
iNumberPrefabController.SpawnForAllyStatic(ally.slotIndex, type, delta);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -256,17 +301,29 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.SetMaxHP(ally.maxHP + delta, false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_up, delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(ally, delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_up, delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(ally, delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy))
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy.SetMaxHP(enemy.maxHP + delta, false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy, PlayerBudeffIconType.ot_maxHP_up, delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(enemy, delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy, PlayerBudeffIconType.ot_maxHP_up, delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(enemy, delta, duration, iconId, enemy.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -280,17 +337,29 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP - delta), false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_down, -delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(ally, -delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_down, -delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(ally, -delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy2))
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy2.SetMaxHP(Mathf.Max(1, enemy2.maxHP - delta), false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy2, PlayerBudeffIconType.ot_maxHP_down, -delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(enemy2, -delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy2, PlayerBudeffIconType.ot_maxHP_down, -delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxHPDeltaCoroutine(enemy2, -delta, duration, iconId, enemy2.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -304,17 +373,29 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.SetMaxMana(ally.maxMana + delta, false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_up, delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(ally, delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_up, delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(ally, delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy3))
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy3.SetMaxMana(enemy3.maxMana + delta, false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy3, PlayerBudeffIconType.ot_maxMana_up, delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(enemy3, delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy3, PlayerBudeffIconType.ot_maxMana_up, delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(enemy3, delta, duration, iconId, enemy3.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -328,17 +409,29 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana - delta), false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_down, -delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(ally, -delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_down, -delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(ally, -delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy4))
|
||||
{
|
||||
int delta = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy4.SetMaxMana(Mathf.Max(1, enemy4.maxMana - delta), false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy4, PlayerBudeffIconType.ot_maxMana_down, -delta, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(enemy4, -delta, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy4, PlayerBudeffIconType.ot_maxMana_down, -delta, duration);
|
||||
StartCoroutine(ApplyTemporaryMaxManaDeltaCoroutine(enemy4, -delta, duration, iconId, enemy4.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -354,11 +447,13 @@ public class EffectSystem : MonoBehaviour
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.scoreEfficiency += amount;
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_up, amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// temporary additive change, revert after duration
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeCoroutine(ally, amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_up, amount, duration);
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeCoroutine(ally, amount, duration, iconId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,10 +469,12 @@ public class EffectSystem : MonoBehaviour
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - amount);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_down, -amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeCoroutine(ally, -amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_down, -amount, duration);
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeCoroutine(ally, -amount, duration, iconId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -395,19 +492,27 @@ public class EffectSystem : MonoBehaviour
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.ModifyAttack(delta);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_up, delta, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(ally, delta, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_up, delta, duration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(ally, delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy5))
|
||||
{
|
||||
int deltaE = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy5.ModifyAttack(deltaE);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy5, PlayerBudeffIconType.ot_atk_up, deltaE, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(enemy5, deltaE, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy5, PlayerBudeffIconType.ot_atk_up, deltaE, duration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(enemy5, deltaE, duration, iconId, enemy5.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -423,20 +528,28 @@ public class EffectSystem : MonoBehaviour
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.ModifyAttack(-delta);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_down, -delta, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply decrease now, revert after duration
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(ally, -delta, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_down, -delta, duration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(ally, -delta, duration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy6))
|
||||
{
|
||||
int deltaE = Mathf.CeilToInt(amount);
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemy6.ModifyAttack(-deltaE);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy6, PlayerBudeffIconType.ot_atk_down, -deltaE, 0f);
|
||||
}
|
||||
else
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(enemy6, -deltaE, duration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy6, PlayerBudeffIconType.ot_atk_down, -deltaE, duration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeCoroutine(enemy6, -deltaE, duration, iconId, enemy6.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -466,27 +579,30 @@ public class EffectSystem : MonoBehaviour
|
||||
var ally = t.GetComponent<AllyCombatant>();
|
||||
if (ally != null)
|
||||
{
|
||||
// amount interpreted as additive delta (0..1). Clamp result 0..0.9 to avoid invulnerability
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.damageResistance = Mathf.Clamp01(ally.damageResistance + amount);
|
||||
ally.damageResistance = ClampDamageResistance(ally.damageResistance + amount);
|
||||
LogVerbose($"[EffectSystem] IncreaseDamageResistance applied to {t.name}: new resistance={ally.damageResistance}");
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_defend_up, amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(ally.gameObject, amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_defend_up, amount, duration);
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(ally.gameObject, amount, duration, iconId, ally.slotIndex, 0));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemyR))
|
||||
{
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemyR.damageResistance = Mathf.Clamp01(enemyR.damageResistance + amount);
|
||||
enemyR.damageResistance = ClampDamageResistance(enemyR.damageResistance + amount);
|
||||
LogVerbose($"[EffectSystem] IncreaseDamageResistance applied to enemy {t.name}: new resistance={enemyR.damageResistance}");
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyR, PlayerBudeffIconType.ot_defend_up, amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(enemyR.gameObject, amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyR, PlayerBudeffIconType.ot_defend_up, amount, duration);
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(enemyR.gameObject, amount, duration, iconId, -1, enemyR.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -501,25 +617,29 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
if (duration <= 0f)
|
||||
{
|
||||
ally.damageResistance = Mathf.Clamp01(ally.damageResistance - amount);
|
||||
ally.damageResistance = ClampDamageResistance(ally.damageResistance - amount);
|
||||
LogVerbose($"[EffectSystem] DecreaseDamageResistance applied to {t.name}: new resistance={ally.damageResistance}");
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_defend_down, -amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply negative delta
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(ally.gameObject, -amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_defend_down, -amount, duration);
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(ally.gameObject, -amount, duration, iconId, ally.slotIndex, 0));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemyR2))
|
||||
{
|
||||
if (duration <= 0f)
|
||||
{
|
||||
enemyR2.damageResistance = Mathf.Clamp01(enemyR2.damageResistance - amount);
|
||||
enemyR2.damageResistance = ClampDamageResistance(enemyR2.damageResistance - amount);
|
||||
LogVerbose($"[EffectSystem] DecreaseDamageResistance applied to enemy {t.name}: new resistance={enemyR2.damageResistance}");
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyR2, PlayerBudeffIconType.ot_defend_down, -amount, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(enemyR2.gameObject, -amount, duration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyR2, PlayerBudeffIconType.ot_defend_down, -amount, duration);
|
||||
StartCoroutine(ApplyTemporaryDamageResistanceChangeCoroutine(enemyR2.gameObject, -amount, duration, iconId, -1, enemyR2.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -548,7 +668,7 @@ public class EffectSystem : MonoBehaviour
|
||||
{
|
||||
int pmDelta = ally.AddScoreForJudge("Perfect");
|
||||
float eff = ally.scoreEfficiency;
|
||||
sm?.AddPmScoreForTrack(slot, pmDelta, eff);
|
||||
sm?.AddPmScoreForTrack(slot, pmDelta, eff, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -870,8 +990,19 @@ public class EffectSystem : MonoBehaviour
|
||||
var comp = target.GetComponent<ICombatant>();
|
||||
if (comp != null)
|
||||
{
|
||||
int beforeHP = 0;
|
||||
if (targetAlly != null) beforeHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) beforeHP = targetEnemy.currentHP;
|
||||
|
||||
comp.ReceiveDamage(amount, source);
|
||||
|
||||
int afterHP = 0;
|
||||
if (targetAlly != null) afterHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) afterHP = targetEnemy.currentHP;
|
||||
|
||||
int actualDelta = beforeHP - afterHP;
|
||||
if (actualDelta != 0) QueueOrSpawnDamagePopup(target, targetAlly, targetEnemy, actualDelta);
|
||||
|
||||
// Record stats if source is an ally
|
||||
if (source != null && target.GetComponent<EnemyCombatant>() != null)
|
||||
{
|
||||
@@ -900,8 +1031,27 @@ public class EffectSystem : MonoBehaviour
|
||||
var comp = target.GetComponent<ICombatant>();
|
||||
if (comp != null)
|
||||
{
|
||||
var targetAlly = target.GetComponent<AllyCombatant>();
|
||||
var targetEnemy = target.GetComponent<EnemyCombatant>();
|
||||
int beforeHP = 0;
|
||||
if (targetAlly != null) beforeHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) beforeHP = targetEnemy.currentHP;
|
||||
|
||||
comp.ReceiveHeal(amount, source);
|
||||
|
||||
int afterHP = 0;
|
||||
if (targetAlly != null) afterHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) afterHP = targetEnemy.currentHP;
|
||||
|
||||
int actualDelta = afterHP - beforeHP;
|
||||
if (actualDelta != 0)
|
||||
{
|
||||
if (targetAlly != null)
|
||||
iNumberPrefabController.SpawnForAllyStatic(targetAlly.slotIndex, iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
else if (targetEnemy != null)
|
||||
iNumberPrefabController.SpawnForEnemyStatic(iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
}
|
||||
|
||||
// Record stats if source is an ally
|
||||
if (source != null)
|
||||
{
|
||||
@@ -918,70 +1068,86 @@ public class EffectSystem : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyDamageOverTimeCoroutine(GameObject target, float totalAmount, float duration, float tickInterval, GameObject source)
|
||||
private IEnumerator ApplyDamageOverTimeCoroutine(GameObject target, float totalAmount, float duration, float tickInterval, GameObject source, string iconId, int allySlotIndex, int enemyInstanceId)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
var comp = target.GetComponent<ICombatant>();
|
||||
if (comp == null)
|
||||
{
|
||||
Debug.LogWarning($"[EffectSystem] Target {target.name} does not implement ICombatant -> cannot apply DOT");
|
||||
yield break;
|
||||
}
|
||||
|
||||
var targetAlly = target.GetComponent<AllyCombatant>();
|
||||
var targetEnemy = target.GetComponent<EnemyCombatant>();
|
||||
// Stop immediately if the target is already dead.
|
||||
if (targetAlly != null && targetAlly.IsDead) yield break;
|
||||
if (targetEnemy != null && (targetEnemy.IsDead || targetEnemy.currentHP <= 0)) yield break;
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
// check death state for immediate damage
|
||||
if (targetEnemy != null && AreAllEnemiesDead())
|
||||
{
|
||||
LogVerbose($"[EffectSystem] Skip DOT (immediate) to {target.name} because all enemies are dead.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
comp.ReceiveDamage(totalAmount, source);
|
||||
// Record stats
|
||||
if (source != null && targetEnemy != null)
|
||||
{
|
||||
var ally = source.GetComponent<AllyCombatant>();
|
||||
if (ally != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamage(ally.slotIndex, totalAmount);
|
||||
}
|
||||
// Record damage taken
|
||||
if (targetAlly != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamageTaken(targetAlly.slotIndex, totalAmount);
|
||||
yield break;
|
||||
}
|
||||
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = totalAmount / ticks;
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
try
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
// check death state inside loop
|
||||
if (targetAlly != null && targetAlly.IsDead) yield break;
|
||||
if (targetEnemy != null && (targetEnemy.IsDead || targetEnemy.currentHP <= 0)) yield break;
|
||||
if (targetEnemy != null && AreAllEnemiesDead())
|
||||
var comp = target.GetComponent<ICombatant>();
|
||||
if (comp == null)
|
||||
{
|
||||
LogVerbose($"[EffectSystem] Stopping DOT to {target.name} because all enemies are dead.");
|
||||
Debug.LogWarning($"[EffectSystem] Target {target.name} does not implement ICombatant -> cannot apply DOT");
|
||||
yield break;
|
||||
}
|
||||
|
||||
comp.ReceiveDamage(perTick, source);
|
||||
// Record stats
|
||||
if (source != null && targetEnemy != null)
|
||||
var targetAlly = target.GetComponent<AllyCombatant>();
|
||||
var targetEnemy = target.GetComponent<EnemyCombatant>();
|
||||
if (targetAlly != null && targetAlly.IsDead) yield break;
|
||||
if (targetEnemy != null && (targetEnemy.IsDead || targetEnemy.currentHP <= 0)) yield break;
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
var ally = source.GetComponent<AllyCombatant>();
|
||||
if (ally != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamage(ally.slotIndex, perTick);
|
||||
if (targetEnemy != null && AreAllEnemiesDead())
|
||||
{
|
||||
LogVerbose($"[EffectSystem] Skip DOT (immediate) to {target.name} because all enemies are dead.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
comp.ReceiveDamage(totalAmount, source);
|
||||
if (source != null && targetEnemy != null)
|
||||
{
|
||||
var ally = source.GetComponent<AllyCombatant>();
|
||||
if (ally != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamage(ally.slotIndex, totalAmount);
|
||||
}
|
||||
if (targetAlly != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamageTaken(targetAlly.slotIndex, totalAmount);
|
||||
yield break;
|
||||
}
|
||||
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = totalAmount / ticks;
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
if (targetAlly != null && targetAlly.IsDead) yield break;
|
||||
if (targetEnemy != null && (targetEnemy.IsDead || targetEnemy.currentHP <= 0)) yield break;
|
||||
if (targetEnemy != null && AreAllEnemiesDead())
|
||||
{
|
||||
LogVerbose($"[EffectSystem] Stopping DOT to {target.name} because all enemies are dead.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
int beforeHP = 0;
|
||||
if (targetAlly != null) beforeHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) beforeHP = targetEnemy.currentHP;
|
||||
|
||||
comp.ReceiveDamage(perTick, source);
|
||||
|
||||
int afterHP = 0;
|
||||
if (targetAlly != null) afterHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) afterHP = targetEnemy.currentHP;
|
||||
|
||||
int actualDelta = beforeHP - afterHP;
|
||||
if (actualDelta != 0) QueueOrSpawnDamagePopup(target, targetAlly, targetEnemy, actualDelta);
|
||||
|
||||
if (source != null && targetEnemy != null)
|
||||
{
|
||||
var ally = source.GetComponent<AllyCombatant>();
|
||||
if (ally != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamage(ally.slotIndex, perTick);
|
||||
}
|
||||
if (targetAlly != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamageTaken(targetAlly.slotIndex, perTick);
|
||||
yield return new WaitForSeconds(tickInterval);
|
||||
elapsed += tickInterval;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconId))
|
||||
{
|
||||
if (allySlotIndex >= 0) iBudeffPrefabController.Instance?.UnregisterTimedEffect(allySlotIndex, iconId);
|
||||
else if (enemyInstanceId != 0) iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
// Record damage taken
|
||||
if (targetAlly != null && teamUIController.Instance != null) teamUIController.Instance.RecordDamageTaken(targetAlly.slotIndex, perTick);
|
||||
yield return new WaitForSeconds(tickInterval);
|
||||
elapsed += tickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,7 +1163,27 @@ public class EffectSystem : MonoBehaviour
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
var targetAlly = target.GetComponent<AllyCombatant>();
|
||||
var targetEnemy = target.GetComponent<EnemyCombatant>();
|
||||
int beforeHP = 0;
|
||||
if (targetAlly != null) beforeHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) beforeHP = targetEnemy.currentHP;
|
||||
|
||||
comp.ReceiveHeal(totalAmount, source);
|
||||
|
||||
int afterHP = 0;
|
||||
if (targetAlly != null) afterHP = targetAlly.currentHP;
|
||||
else if (targetEnemy != null) afterHP = targetEnemy.currentHP;
|
||||
|
||||
int actualDelta = afterHP - beforeHP;
|
||||
if (actualDelta != 0)
|
||||
{
|
||||
if (targetAlly != null)
|
||||
iNumberPrefabController.SpawnForAllyStatic(targetAlly.slotIndex, iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
else if (targetEnemy != null)
|
||||
iNumberPrefabController.SpawnForEnemyStatic(iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
}
|
||||
|
||||
// Record stats
|
||||
if (source != null)
|
||||
{
|
||||
@@ -1010,10 +1196,31 @@ public class EffectSystem : MonoBehaviour
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = totalAmount / ticks;
|
||||
float elapsed = 0f;
|
||||
var targetAllyHot = target.GetComponent<AllyCombatant>();
|
||||
var targetEnemyHot = target.GetComponent<EnemyCombatant>();
|
||||
while (elapsed < duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
int beforeHP = 0;
|
||||
if (targetAllyHot != null) beforeHP = targetAllyHot.currentHP;
|
||||
else if (targetEnemyHot != null) beforeHP = targetEnemyHot.currentHP;
|
||||
|
||||
comp.ReceiveHeal(perTick, source);
|
||||
|
||||
int afterHP = 0;
|
||||
if (targetAllyHot != null) afterHP = targetAllyHot.currentHP;
|
||||
else if (targetEnemyHot != null) afterHP = targetEnemyHot.currentHP;
|
||||
|
||||
int actualDelta = afterHP - beforeHP;
|
||||
if (actualDelta != 0)
|
||||
{
|
||||
if (targetAllyHot != null)
|
||||
iNumberPrefabController.SpawnForAllyStatic(targetAllyHot.slotIndex, iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
else if (targetEnemyHot != null)
|
||||
iNumberPrefabController.SpawnForEnemyStatic(iNumberPrefabController.InstantNumberType.Heal, Mathf.Abs(actualDelta));
|
||||
}
|
||||
|
||||
// Record stats
|
||||
if (source != null)
|
||||
{
|
||||
@@ -1025,6 +1232,63 @@ public class EffectSystem : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkAwaitingHitFx(GameObject target)
|
||||
{
|
||||
if (target == null) return;
|
||||
int id = target.GetInstanceID();
|
||||
_awaitingHitFxTargets.Add(id);
|
||||
}
|
||||
|
||||
private void FlushPendingDamagePopupAfterHit(GameObject target)
|
||||
{
|
||||
if (target == null) return;
|
||||
int id = target.GetInstanceID();
|
||||
if (_awaitingHitFxTargets.Contains(id)) _awaitingHitFxTargets.Remove(id);
|
||||
|
||||
if (!_pendingDamagePopupByTargetId.TryGetValue(id, out var signedValue) || signedValue == 0) return;
|
||||
_pendingDamagePopupByTargetId.Remove(id);
|
||||
|
||||
var ally = target.GetComponent<AllyCombatant>();
|
||||
if (ally != null)
|
||||
{
|
||||
iNumberPrefabController.SpawnForAllyStatic(ally.slotIndex, iNumberPrefabController.InstantNumberType.Damage, signedValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.GetComponent<EnemyCombatant>() != null)
|
||||
{
|
||||
iNumberPrefabController.SpawnForEnemyStatic(iNumberPrefabController.InstantNumberType.Damage, signedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueOrSpawnDamagePopup(GameObject target, AllyCombatant targetAlly, EnemyCombatant targetEnemy, int actualDelta)
|
||||
{
|
||||
if (target == null) return;
|
||||
if (actualDelta == 0) return;
|
||||
int id = target.GetInstanceID();
|
||||
int signedValue = -Mathf.Abs(actualDelta);
|
||||
|
||||
if (_awaitingHitFxTargets.Contains(id))
|
||||
{
|
||||
if (_pendingDamagePopupByTargetId.TryGetValue(id, out var prev))
|
||||
_pendingDamagePopupByTargetId[id] = prev + signedValue;
|
||||
else
|
||||
_pendingDamagePopupByTargetId[id] = signedValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetAlly != null)
|
||||
{
|
||||
iNumberPrefabController.SpawnForAllyStatic(targetAlly.slotIndex, iNumberPrefabController.InstantNumberType.Damage, signedValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEnemy != null)
|
||||
{
|
||||
iNumberPrefabController.SpawnForEnemyStatic(iNumberPrefabController.InstantNumberType.Damage, signedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyIncreaseManaOverTimeCoroutine(GameObject target, float totalAmount, float duration, float tickInterval, GameObject source)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
@@ -1042,7 +1306,14 @@ public class EffectSystem : MonoBehaviour
|
||||
// immediate add totalAmount to mana if ally present
|
||||
if (ally != null)
|
||||
{
|
||||
int before = ally.currentMana;
|
||||
ally.ModifyMana(Mathf.CeilToInt(totalAmount), true, true);
|
||||
int delta = ally.currentMana - before;
|
||||
if (delta != 0)
|
||||
{
|
||||
var type = delta > 0 ? iNumberPrefabController.InstantNumberType.ManaPlus : iNumberPrefabController.InstantNumberType.ManaMinus;
|
||||
iNumberPrefabController.SpawnForAllyStatic(ally.slotIndex, type, delta);
|
||||
}
|
||||
// Record stats
|
||||
if (totalAmount > 0f && source != null)
|
||||
{
|
||||
@@ -1061,7 +1332,14 @@ public class EffectSystem : MonoBehaviour
|
||||
if (target == null) yield break;
|
||||
if (ally != null)
|
||||
{
|
||||
int before = ally.currentMana;
|
||||
ally.ModifyMana(Mathf.CeilToInt(perTick), true, true);
|
||||
int delta = ally.currentMana - before;
|
||||
if (delta != 0)
|
||||
{
|
||||
var type = delta > 0 ? iNumberPrefabController.InstantNumberType.ManaPlus : iNumberPrefabController.InstantNumberType.ManaMinus;
|
||||
iNumberPrefabController.SpawnForAllyStatic(ally.slotIndex, type, delta);
|
||||
}
|
||||
// Record stats
|
||||
if (perTick > 0f && source != null)
|
||||
{
|
||||
@@ -1106,93 +1384,170 @@ public class EffectSystem : MonoBehaviour
|
||||
}
|
||||
|
||||
// Coroutine to apply temporary scoreEfficiency change and revert after duration
|
||||
private IEnumerator ApplyTemporaryScoreEfficiencyChangeCoroutine(AllyCombatant ally, float delta, float duration)
|
||||
private IEnumerator ApplyTemporaryScoreEfficiencyChangeCoroutine(AllyCombatant ally, float delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - delta);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutine to apply temporary damageResistance change and revert after duration
|
||||
private IEnumerator ApplyTemporaryDamageResistanceChangeCoroutine(GameObject target, float delta, float duration)
|
||||
private IEnumerator ApplyTemporaryDamageResistanceChangeCoroutine(GameObject target, float delta, float duration, string iconId = null, int allySlotIndex = -1, int enemyInstanceId = 0)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
// support both ally and enemy
|
||||
var ally = target.GetComponent<AllyCombatant>();
|
||||
if (ally != null)
|
||||
try
|
||||
{
|
||||
float before = ally.damageResistance;
|
||||
ally.damageResistance = Mathf.Clamp01(ally.damageResistance + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
// revert by subtracting delta, clamp
|
||||
ally.damageResistance = Mathf.Clamp01(ally.damageResistance - delta);
|
||||
yield break;
|
||||
}
|
||||
if (target == null) yield break;
|
||||
var ally = target.GetComponent<AllyCombatant>();
|
||||
if (ally != null)
|
||||
{
|
||||
ally.damageResistance = ClampDamageResistance(ally.damageResistance + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.damageResistance = ClampDamageResistance(ally.damageResistance - delta);
|
||||
yield break;
|
||||
}
|
||||
|
||||
var enemy = target.GetComponent<EnemyCombatant>();
|
||||
if (enemy != null)
|
||||
var enemy = target.GetComponent<EnemyCombatant>();
|
||||
if (enemy != null)
|
||||
{
|
||||
enemy.damageResistance = ClampDamageResistance(enemy.damageResistance + delta);
|
||||
iBudeffPrefabController.Instance?.RefreshEnemyNow(enemy);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.damageResistance = ClampDamageResistance(enemy.damageResistance - delta);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
float beforeE = enemy.damageResistance;
|
||||
enemy.damageResistance = Mathf.Clamp01(enemy.damageResistance + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
enemy.damageResistance = Mathf.Clamp01(enemy.damageResistance - delta);
|
||||
yield break;
|
||||
if (!string.IsNullOrEmpty(iconId))
|
||||
{
|
||||
if (allySlotIndex >= 0) iBudeffPrefabController.Instance?.UnregisterTimedEffect(allySlotIndex, iconId);
|
||||
else if (enemyInstanceId != 0) iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutine to apply temporary attack change for AllyCombatant
|
||||
private IEnumerator ApplyTemporaryAttackChangeCoroutine(AllyCombatant ally, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryAttackChangeCoroutine(AllyCombatant ally, int delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
ally.ModifyAttack(-delta);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(-delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private static float ClampDamageResistance(float value)
|
||||
{
|
||||
return Mathf.Min(value, 1f);
|
||||
}
|
||||
|
||||
// Coroutine to apply temporary attack change for EnemyCombatant
|
||||
private IEnumerator ApplyTemporaryAttackChangeCoroutine(EnemyCombatant enemy, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryAttackChangeCoroutine(EnemyCombatant enemy, int delta, float duration, string iconId, int enemyInstanceId)
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
enemy.ModifyAttack(-delta);
|
||||
try
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(delta);
|
||||
iBudeffPrefabController.Instance?.RefreshEnemyNow(enemy);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(-delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconId) && enemyInstanceId != 0)
|
||||
iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryMaxHPDeltaCoroutine(AllyCombatant ally, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryMaxHPDeltaCoroutine(AllyCombatant ally, int delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP - delta), false);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP - delta), false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryMaxHPDeltaCoroutine(EnemyCombatant enemy, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryMaxHPDeltaCoroutine(EnemyCombatant enemy, int delta, float duration, string iconId, int enemyInstanceId)
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxHP(Mathf.Max(1, enemy.maxHP + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxHP(Mathf.Max(1, enemy.maxHP - delta), false);
|
||||
try
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxHP(Mathf.Max(1, enemy.maxHP + delta), false);
|
||||
iBudeffPrefabController.Instance?.RefreshEnemyNow(enemy);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxHP(Mathf.Max(1, enemy.maxHP - delta), false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconId) && enemyInstanceId != 0)
|
||||
iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryMaxManaDeltaCoroutine(AllyCombatant ally, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryMaxManaDeltaCoroutine(AllyCombatant ally, int delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana - delta), false);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana - delta), false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryMaxManaDeltaCoroutine(EnemyCombatant enemy, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryMaxManaDeltaCoroutine(EnemyCombatant enemy, int delta, float duration, string iconId, int enemyInstanceId)
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana + delta), false);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana - delta), false);
|
||||
try
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana + delta), false);
|
||||
iBudeffPrefabController.Instance?.RefreshEnemyNow(enemy);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana - delta), false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconId) && enemyInstanceId != 0)
|
||||
iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -64,6 +64,8 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
currentMana = 0;
|
||||
isDead = false;
|
||||
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyBaseline(this);
|
||||
|
||||
// notify listeners (UI/manager) that enemy revived
|
||||
OnEnemyRevived?.Invoke(this);
|
||||
|
||||
@@ -158,9 +160,11 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
public void ApplyBuff(Buff buff, GameObject source)
|
||||
{
|
||||
if (buff == null) return;
|
||||
Debug.LogWarning($"[EnemyCombatant] ApplyBuff: {buff.description} (id={buff.buffId}) to {name}. AtkMult={buff.attackMultiplier}, HealMult={buff.healReceivedMultiplier}, ScoreMult={buff.scoreMultiplier}");
|
||||
activeBuffs.Add(buff);
|
||||
// Apply reversible multipliers. attackMultiplier is applied via baseline+recalc to ensure it can be reverted.
|
||||
if (buff.attackMultiplier != 1f) RecalculateAttackFromBuffs();
|
||||
iBudeffPrefabController.Instance?.NotifyEnemyBuffApplied(this, buff);
|
||||
}
|
||||
|
||||
public void RemoveBuff(string buffId)
|
||||
@@ -170,9 +174,22 @@ public class EnemyCombatant : MonoBehaviour, ICombatant
|
||||
{
|
||||
activeBuffs.Remove(b);
|
||||
if (b.attackMultiplier != 1f) RecalculateAttackFromBuffs();
|
||||
iBudeffPrefabController.Instance?.NotifyEnemyBuffRemoved(this, b);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetHealReceivedMultiplierForUI()
|
||||
{
|
||||
float mult = 1f;
|
||||
for (int i = 0; i < activeBuffs.Count; i++)
|
||||
{
|
||||
var b = activeBuffs[i];
|
||||
if (b == null) continue;
|
||||
mult *= b.healReceivedMultiplier;
|
||||
}
|
||||
return mult;
|
||||
}
|
||||
|
||||
private void EnsureAttackBaseInitialized()
|
||||
{
|
||||
if (_attackBaseInitialized) return;
|
||||
|
||||
@@ -363,7 +363,7 @@ public class ScoreManager : MonoBehaviour
|
||||
/// Add a per-note pm score delta to the specified track (0..4) and update named fields + aggregate.
|
||||
/// Also calculates idol score as pmDelta * scoreEfficiency.
|
||||
/// </summary>
|
||||
public void AddPmScoreForTrack(int trackIndex, int pmDelta, float scoreEfficiency)
|
||||
public void AddPmScoreForTrack(int trackIndex, int pmDelta, float scoreEfficiency, bool isSkillRelated = false)
|
||||
{
|
||||
TryHookPerfectBonusEvent();
|
||||
if (trackIndex < 0 || trackIndex >= pmScoreSums.Length) return;
|
||||
@@ -373,11 +373,19 @@ public class ScoreManager : MonoBehaviour
|
||||
if (pmScoreSums[trackIndex] < 0) pmScoreSums[trackIndex] = 0;
|
||||
if (pmScoreSums[trackIndex] > int.MaxValue - 1) pmScoreSums[trackIndex] = int.MaxValue - 1;
|
||||
|
||||
// calculate idol score: pmDelta * scoreEfficiency
|
||||
int idolBefore = idolScoreSums[trackIndex];
|
||||
int idolDelta = Mathf.FloorToInt(pmDelta * scoreEfficiency);
|
||||
idolScoreSums[trackIndex] += idolDelta;
|
||||
if (idolScoreSums[trackIndex] < 0) idolScoreSums[trackIndex] = 0;
|
||||
if (idolScoreSums[trackIndex] > int.MaxValue - 1) idolScoreSums[trackIndex] = int.MaxValue - 1;
|
||||
int idolActualDelta = idolScoreSums[trackIndex] - idolBefore;
|
||||
|
||||
// Only spawn popup if it is skill-related
|
||||
if (isSkillRelated && idolActualDelta != 0)
|
||||
{
|
||||
var type = idolActualDelta > 0 ? iNumberPrefabController.InstantNumberType.IscorePlus : iNumberPrefabController.InstantNumberType.IscoreMinus;
|
||||
iNumberPrefabController.SpawnForAllyStatic(trackIndex, type, idolActualDelta);
|
||||
}
|
||||
|
||||
// update named fields for easy access
|
||||
red_pmScore_sum = pmScoreSums[0];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -112,6 +112,9 @@ public class SkillBuilder : MonoBehaviour
|
||||
public EffectType effectType;
|
||||
public float floatDelta;
|
||||
public int intDelta;
|
||||
public string budeffIconId;
|
||||
public int budeffSlotIndex = -1;
|
||||
public int budeffEnemyInstanceId;
|
||||
}
|
||||
|
||||
private void PrewarmAllyHeroSOIndex()
|
||||
@@ -205,6 +208,12 @@ public class SkillBuilder : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
// Log Miss event to feed (since HP loss will occur)
|
||||
string mName = "Unknown";
|
||||
if (so != null && !string.IsNullOrEmpty(so.ally_heroName)) mName = so.ally_heroName;
|
||||
else if (ally != null) mName = ally.name; // fallback to GameObject name
|
||||
SkillTriggerFeedUI.PushMiss(mName);
|
||||
|
||||
float missBase = (levelInfo != null) ? levelInfo.missHpLossBase : missHpLossBase;
|
||||
|
||||
// 播放敌人攻击特效,并将伤害逻辑延迟到特效到达时执行
|
||||
@@ -751,6 +760,30 @@ public class SkillBuilder : MonoBehaviour
|
||||
return def.skillId + ":" + target.GetInstanceID();
|
||||
}
|
||||
|
||||
private static bool ShouldSpawnApprForEffect(EffectType effectType)
|
||||
{
|
||||
switch (effectType)
|
||||
{
|
||||
case EffectType.DamageOverTimeAlly:
|
||||
case EffectType.ReduceEnemyHealOverTime:
|
||||
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.IncreaseAttack:
|
||||
case EffectType.DecreaseAttack:
|
||||
case EffectType.RedirectNextDamageToSelf:
|
||||
case EffectType.RedirectSelfDamageToAdjacent:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryApplyRefreshOnlyTimedEffect(SkillDefinition def, GameObject target, float amount, float duration)
|
||||
{
|
||||
if (!IsRefreshOnlyTimedSkill(def)) return false;
|
||||
@@ -773,6 +806,41 @@ public class SkillBuilder : MonoBehaviour
|
||||
return false;
|
||||
}
|
||||
|
||||
var ally = target.GetComponent<AllyCombatant>() ?? target.GetComponentInChildren<AllyCombatant>(true);
|
||||
var enemy = target.GetComponent<EnemyCombatant>() ?? target.GetComponentInChildren<EnemyCombatant>(true);
|
||||
if (iBudeffPrefabController.Instance != null)
|
||||
{
|
||||
PlayerBudeffIconType? iconType = null;
|
||||
switch (def.effectType)
|
||||
{
|
||||
case EffectType.IncreaseScoreEfficiency: iconType = PlayerBudeffIconType.ot_scoreEfficiency_up; break;
|
||||
case EffectType.DecreaseScoreEfficiency: iconType = PlayerBudeffIconType.ot_scoreEfficiency_down; break;
|
||||
case EffectType.IncreaseDamageResistance: iconType = PlayerBudeffIconType.ot_defend_up; break;
|
||||
case EffectType.DecreaseDamageResistance: iconType = PlayerBudeffIconType.ot_defend_down; break;
|
||||
case EffectType.IncreaseAttack: iconType = PlayerBudeffIconType.ot_atk_up; break;
|
||||
case EffectType.DecreaseAttack: iconType = PlayerBudeffIconType.ot_atk_down; break;
|
||||
case EffectType.IncreaseMaxHP: iconType = PlayerBudeffIconType.ot_maxHP_up; break;
|
||||
case EffectType.DecreaseMaxHP: iconType = PlayerBudeffIconType.ot_maxHP_down; break;
|
||||
case EffectType.IncreaseMaxMana: iconType = PlayerBudeffIconType.ot_maxMana_up; break;
|
||||
case EffectType.DecreaseMaxMana: iconType = PlayerBudeffIconType.ot_maxMana_down; break;
|
||||
}
|
||||
|
||||
if (iconType.HasValue)
|
||||
{
|
||||
float v = applied.intDelta != 0 ? applied.intDelta : applied.floatDelta;
|
||||
if (ally != null)
|
||||
{
|
||||
applied.budeffSlotIndex = ally.slotIndex;
|
||||
applied.budeffIconId = iBudeffPrefabController.Instance.RegisterTimedEffect(ally, iconType.Value, v, duration);
|
||||
}
|
||||
else if (enemy != null && def.effectType != EffectType.IncreaseScoreEfficiency && def.effectType != EffectType.DecreaseScoreEfficiency)
|
||||
{
|
||||
applied.budeffEnemyInstanceId = enemy.GetInstanceID();
|
||||
applied.budeffIconId = iBudeffPrefabController.Instance.RegisterEnemyTimedEffect(enemy, iconType.Value, v, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_refreshOnlyTimedEffectStates[key] = applied;
|
||||
_refreshOnlyTimedEffectCoroutines[key] = StartCoroutine(RemoveRefreshOnlyTimedEffectAfterDuration(key, target, duration));
|
||||
return true;
|
||||
@@ -798,21 +866,28 @@ public class SkillBuilder : MonoBehaviour
|
||||
var enemy = target != null ? target.GetComponent<EnemyCombatant>() : null;
|
||||
if (ally == null && enemy == null) return false;
|
||||
|
||||
string targetName = ally != null ? ally.allyName : (enemy != null && enemy.sourceData != null ? enemy.sourceData.enemyName : target.name);
|
||||
|
||||
switch (effectType)
|
||||
{
|
||||
case EffectType.IncreaseScoreEfficiency:
|
||||
case EffectType.DecreaseScoreEfficiency:
|
||||
if (ally == null) return false;
|
||||
float scoreDelta = effectType == EffectType.IncreaseScoreEfficiency ? amount : -amount;
|
||||
float oldScore = ally.scoreEfficiency;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency + scoreDelta);
|
||||
CheckLimitAndWarn(targetName, "分数效率", oldScore, ally.scoreEfficiency, scoreDelta);
|
||||
state = new RefreshOnlyTimedState { effectType = effectType, floatDelta = scoreDelta };
|
||||
return true;
|
||||
|
||||
case EffectType.IncreaseDamageResistance:
|
||||
case EffectType.DecreaseDamageResistance:
|
||||
float resistDelta = effectType == EffectType.IncreaseDamageResistance ? amount : -amount;
|
||||
if (ally != null) ally.damageResistance = Mathf.Clamp01(ally.damageResistance + resistDelta);
|
||||
else enemy.damageResistance = Mathf.Clamp01(enemy.damageResistance + resistDelta);
|
||||
float oldResist = ally != null ? ally.damageResistance : enemy.damageResistance;
|
||||
if (ally != null) ally.damageResistance = ClampDamageResistance(ally.damageResistance + resistDelta);
|
||||
else enemy.damageResistance = ClampDamageResistance(enemy.damageResistance + resistDelta);
|
||||
float newResist = ally != null ? ally.damageResistance : enemy.damageResistance;
|
||||
CheckLimitAndWarn(targetName, "伤害抗性", oldResist, newResist, resistDelta);
|
||||
state = new RefreshOnlyTimedState { effectType = effectType, floatDelta = resistDelta };
|
||||
return true;
|
||||
|
||||
@@ -820,8 +895,11 @@ public class SkillBuilder : MonoBehaviour
|
||||
case EffectType.DecreaseAttack:
|
||||
int atkDelta = Mathf.CeilToInt(Mathf.Abs(amount));
|
||||
if (effectType == EffectType.DecreaseAttack) atkDelta = -atkDelta;
|
||||
int oldAtk = ally != null ? ally.attack : enemy.attack;
|
||||
if (ally != null) ally.ModifyAttack(atkDelta);
|
||||
else enemy.ModifyAttack(atkDelta);
|
||||
int newAtk = ally != null ? ally.attack : enemy.attack;
|
||||
CheckLimitAndWarnInt(targetName, "攻击力", oldAtk, newAtk, atkDelta);
|
||||
state = new RefreshOnlyTimedState { effectType = effectType, intDelta = atkDelta };
|
||||
return true;
|
||||
|
||||
@@ -829,8 +907,11 @@ public class SkillBuilder : MonoBehaviour
|
||||
case EffectType.DecreaseMaxHP:
|
||||
int hpDelta = Mathf.CeilToInt(Mathf.Abs(amount));
|
||||
if (effectType == EffectType.DecreaseMaxHP) hpDelta = -hpDelta;
|
||||
int oldHP = ally != null ? ally.maxHP : enemy.maxHP;
|
||||
if (ally != null) ally.SetMaxHP(Mathf.Max(1, ally.maxHP + hpDelta), false);
|
||||
else enemy.SetMaxHP(Mathf.Max(1, enemy.maxHP + hpDelta), false);
|
||||
int newHP = ally != null ? ally.maxHP : enemy.maxHP;
|
||||
CheckLimitAndWarnInt(targetName, "最大生命值", oldHP, newHP, hpDelta);
|
||||
state = new RefreshOnlyTimedState { effectType = effectType, intDelta = hpDelta };
|
||||
return true;
|
||||
|
||||
@@ -838,8 +919,11 @@ public class SkillBuilder : MonoBehaviour
|
||||
case EffectType.DecreaseMaxMana:
|
||||
int manaDelta = Mathf.CeilToInt(Mathf.Abs(amount));
|
||||
if (effectType == EffectType.DecreaseMaxMana) manaDelta = -manaDelta;
|
||||
int oldMana = ally != null ? ally.maxMana : enemy.maxMana;
|
||||
if (ally != null) ally.SetMaxMana(Mathf.Max(1, ally.maxMana + manaDelta), false);
|
||||
else enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana + manaDelta), false);
|
||||
int newMana = ally != null ? ally.maxMana : enemy.maxMana;
|
||||
CheckLimitAndWarnInt(targetName, "最大法力值", oldMana, newMana, manaDelta);
|
||||
state = new RefreshOnlyTimedState { effectType = effectType, intDelta = manaDelta };
|
||||
return true;
|
||||
}
|
||||
@@ -847,6 +931,30 @@ public class SkillBuilder : MonoBehaviour
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CheckLimitAndWarn(string targetName, string attrName, float startValue, float newValue, float attemptedDelta)
|
||||
{
|
||||
float actualChange = newValue - startValue;
|
||||
// If we attempted a change but the actual change differs significantly from attempted, we hit a limit.
|
||||
if (Mathf.Abs(attemptedDelta) > 0.001f && Mathf.Abs(actualChange - attemptedDelta) > 0.001f)
|
||||
{
|
||||
load_skillwarn.Instance?.ShowWarning(targetName, attrName);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckLimitAndWarnInt(string targetName, string attrName, int startValue, int newValue, int attemptedDelta)
|
||||
{
|
||||
int actualChange = newValue - startValue;
|
||||
if (attemptedDelta != 0 && actualChange != attemptedDelta)
|
||||
{
|
||||
load_skillwarn.Instance?.ShowWarning(targetName, attrName);
|
||||
}
|
||||
}
|
||||
|
||||
private static float ClampDamageResistance(float value)
|
||||
{
|
||||
return Mathf.Min(value, 1f);
|
||||
}
|
||||
|
||||
private void RevertRefreshOnlyTimedEffect(GameObject target, RefreshOnlyTimedState state)
|
||||
{
|
||||
if (target == null || state == null) return;
|
||||
@@ -864,8 +972,8 @@ public class SkillBuilder : MonoBehaviour
|
||||
|
||||
case EffectType.IncreaseDamageResistance:
|
||||
case EffectType.DecreaseDamageResistance:
|
||||
if (ally != null) ally.damageResistance = Mathf.Clamp01(ally.damageResistance - state.floatDelta);
|
||||
else enemy.damageResistance = Mathf.Clamp01(enemy.damageResistance - state.floatDelta);
|
||||
if (ally != null) ally.damageResistance = ClampDamageResistance(ally.damageResistance - state.floatDelta);
|
||||
else enemy.damageResistance = ClampDamageResistance(enemy.damageResistance - state.floatDelta);
|
||||
break;
|
||||
|
||||
case EffectType.IncreaseAttack:
|
||||
@@ -886,6 +994,15 @@ public class SkillBuilder : MonoBehaviour
|
||||
else enemy.SetMaxMana(Mathf.Max(1, enemy.maxMana - state.intDelta), false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(state.budeffIconId))
|
||||
{
|
||||
if (state.budeffSlotIndex >= 0) iBudeffPrefabController.Instance?.UnregisterTimedEffect(state.budeffSlotIndex, state.budeffIconId);
|
||||
else if (state.budeffEnemyInstanceId != 0) iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(state.budeffEnemyInstanceId, state.budeffIconId);
|
||||
state.budeffIconId = null;
|
||||
state.budeffSlotIndex = -1;
|
||||
state.budeffEnemyInstanceId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryApplyRefreshOnlyOverTimeEffect(SkillDefinition def, GameObject target, float totalAmount, GameObject source)
|
||||
@@ -973,15 +1090,15 @@ public class SkillBuilder : MonoBehaviour
|
||||
{
|
||||
if (def == null) { Debug.LogWarning("UseSkillDefinition: null def"); return; }
|
||||
|
||||
int groupId = 0;
|
||||
string smallSkillName = null;
|
||||
Sprite smallSkillIcon = null;
|
||||
// Resolve group/icon metadata for the ally HUD skill icon queue.
|
||||
try
|
||||
{
|
||||
var heroSoForName = GetAllyHeroSOBySlot(slotIndex);
|
||||
|
||||
// Documentation text normalized.
|
||||
int groupId = 0;
|
||||
string smallSkillName = null;
|
||||
Sprite smallSkillIcon = null;
|
||||
if (heroSoForName != null && heroSoForName.skillGroups != null)
|
||||
{
|
||||
foreach (var g in heroSoForName.skillGroups)
|
||||
@@ -1043,6 +1160,13 @@ ResolvedGroup:
|
||||
if (!s_lastSkillFeedFrameByKey.TryGetValue(key, out lastFrame) || lastFrame != frame)
|
||||
{
|
||||
s_lastSkillFeedFrameByKey[key] = frame;
|
||||
|
||||
// Push to text feed
|
||||
if (heroSoForName != null && !string.IsNullOrEmpty(heroSoForName.ally_heroName))
|
||||
{
|
||||
SkillTriggerFeedUI.Push(heroSoForName.ally_heroName, smallSkillName);
|
||||
}
|
||||
|
||||
// Ally HUD skill icon feed (newest first, max 3).
|
||||
if (smallSkillIcon != null && slotIndex >= 0 && slotIndex < 5)
|
||||
{
|
||||
@@ -1247,6 +1371,12 @@ ResolvedGroup:
|
||||
var ally = t.GetComponent<AllyCombatant>();
|
||||
var ic = t.GetComponent<ICombatant>();
|
||||
|
||||
if (ally != null && ShouldSpawnApprForEffect(def.effectType))
|
||||
{
|
||||
var apprSprite = iBudeffPrefabController.Instance != null ? iBudeffPrefabController.Instance.GetSpriteForEffect(def.effectType) : null;
|
||||
if (apprSprite != null) iBudeffPrefabController.Instance?.SpawnApprForSlot(ally.slotIndex, apprSprite);
|
||||
}
|
||||
|
||||
switch (def.effectType)
|
||||
{
|
||||
case EffectType.DamageSingleEnemy:
|
||||
@@ -1263,7 +1393,25 @@ ResolvedGroup:
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(ApplyDamageOverTimeDirect(t, amountTotal, def.defaultDuration, def.GetEffectiveTickInterval(), caster));
|
||||
string iconId = null;
|
||||
int allySlotIndex = -1;
|
||||
int enemyInstanceId = 0;
|
||||
var enemyDot = t.GetComponent<EnemyCombatant>() ?? t.GetComponentInChildren<EnemyCombatant>(true);
|
||||
if (enemyDot != null)
|
||||
{
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(def.defaultDuration / def.GetEffectiveTickInterval()));
|
||||
float perTick = amountTotal / ticks;
|
||||
iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemyDot, PlayerBudeffIconType.ot_bleeding, perTick, def.defaultDuration);
|
||||
enemyInstanceId = enemyDot.GetInstanceID();
|
||||
}
|
||||
else if (ally != null)
|
||||
{
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(def.defaultDuration / def.GetEffectiveTickInterval()));
|
||||
float perTick = amountTotal / ticks;
|
||||
iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_bleeding, perTick, def.defaultDuration);
|
||||
allySlotIndex = ally.slotIndex;
|
||||
}
|
||||
StartCoroutine(ApplyDamageOverTimeDirect(t, amountTotal, def.defaultDuration, def.GetEffectiveTickInterval(), caster, iconId, allySlotIndex, enemyInstanceId));
|
||||
}
|
||||
break;
|
||||
case EffectType.HealSingleSelf:
|
||||
@@ -1332,20 +1480,60 @@ ResolvedGroup:
|
||||
break;
|
||||
// Documentation text normalized.
|
||||
case EffectType.IncreaseMaxHP:
|
||||
if (ally != null) ally.SetMaxHP(ally.maxHP + Mathf.CeilToInt(amountTotal), false);
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy)) enemy.SetMaxHP(enemy.maxHP + Mathf.CeilToInt(amountTotal), false);
|
||||
if (ally != null)
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
ally.SetMaxHP(ally.maxHP + d, false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_up, d, 0f);
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy))
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
enemy.SetMaxHP(enemy.maxHP + d, false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy, PlayerBudeffIconType.ot_maxHP_up, d, 0f);
|
||||
}
|
||||
break;
|
||||
case EffectType.DecreaseMaxHP:
|
||||
if (ally != null) ally.SetMaxHP(Mathf.Max(1, ally.maxHP - Mathf.CeilToInt(amountTotal)), false);
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy2)) enemy2.SetMaxHP(Mathf.Max(1, enemy2.maxHP - Mathf.CeilToInt(amountTotal)), false);
|
||||
if (ally != null)
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
ally.SetMaxHP(Mathf.Max(1, ally.maxHP - d), false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxHP_down, -d, 0f);
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy2))
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
enemy2.SetMaxHP(Mathf.Max(1, enemy2.maxHP - d), false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy2, PlayerBudeffIconType.ot_maxHP_down, -d, 0f);
|
||||
}
|
||||
break;
|
||||
case EffectType.IncreaseMaxMana:
|
||||
if (ally != null) ally.SetMaxMana(ally.maxMana + Mathf.CeilToInt(amountTotal), false);
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy3)) enemy3.SetMaxMana(enemy3.maxMana + Mathf.CeilToInt(amountTotal), false);
|
||||
if (ally != null)
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
ally.SetMaxMana(ally.maxMana + d, false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_up, d, 0f);
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy3))
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
enemy3.SetMaxMana(enemy3.maxMana + d, false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy3, PlayerBudeffIconType.ot_maxMana_up, d, 0f);
|
||||
}
|
||||
break;
|
||||
case EffectType.DecreaseMaxMana:
|
||||
if (ally != null) ally.SetMaxMana(Mathf.Max(1, ally.maxMana - Mathf.CeilToInt(amountTotal)), false);
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy4)) enemy4.SetMaxMana(Mathf.Max(1, enemy4.maxMana - Mathf.CeilToInt(amountTotal)), false);
|
||||
if (ally != null)
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
ally.SetMaxMana(Mathf.Max(1, ally.maxMana - d), false);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_maxMana_down, -d, 0f);
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy4))
|
||||
{
|
||||
int d = Mathf.CeilToInt(amountTotal);
|
||||
enemy4.SetMaxMana(Mathf.Max(1, enemy4.maxMana - d), false);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy4, PlayerBudeffIconType.ot_maxMana_down, -d, 0f);
|
||||
}
|
||||
break;
|
||||
case EffectType.IncreaseScoreEfficiency:
|
||||
if (ally != null)
|
||||
@@ -1353,19 +1541,28 @@ ResolvedGroup:
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
ally.scoreEfficiency += amountTotal;
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_up, amountTotal, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
{
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeDirect(ally, amountTotal, def.defaultDuration));
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_up, amountTotal, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeDirect(ally, amountTotal, def.defaultDuration, iconId));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EffectType.DecreaseScoreEfficiency:
|
||||
if (ally != null)
|
||||
{
|
||||
if (def.defaultDuration <= 0f) ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - amountTotal);
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - amountTotal);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_down, -amountTotal, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeDirect(ally, -amountTotal, def.defaultDuration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_scoreEfficiency_down, -amountTotal, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryScoreEfficiencyChangeDirect(ally, -amountTotal, def.defaultDuration, iconId));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EffectType.IncreaseAttack:
|
||||
@@ -1373,15 +1570,29 @@ ResolvedGroup:
|
||||
int delta = Mathf.CeilToInt(amountTotal);
|
||||
if (ally != null)
|
||||
{
|
||||
if (def.defaultDuration <= 0f) ally.ModifyAttack(delta);
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
ally.ModifyAttack(delta);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_up, delta, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(ally, delta, def.defaultDuration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_up, delta, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(ally, delta, def.defaultDuration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy5))
|
||||
{
|
||||
if (def.defaultDuration <= 0f) enemy5.ModifyAttack(delta);
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
enemy5.ModifyAttack(delta);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy5, PlayerBudeffIconType.ot_atk_up, delta, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(enemy5, delta, def.defaultDuration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy5, PlayerBudeffIconType.ot_atk_up, delta, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(enemy5, delta, def.defaultDuration, iconId, enemy5.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1390,15 +1601,29 @@ ResolvedGroup:
|
||||
int delta = Mathf.CeilToInt(amountTotal);
|
||||
if (ally != null)
|
||||
{
|
||||
if (def.defaultDuration <= 0f) ally.ModifyAttack(-delta);
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
ally.ModifyAttack(-delta);
|
||||
iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_down, -delta, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(ally, -delta, def.defaultDuration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(ally, PlayerBudeffIconType.ot_atk_down, -delta, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(ally, -delta, def.defaultDuration, iconId));
|
||||
}
|
||||
}
|
||||
else if (t.TryGetComponent<EnemyCombatant>(out var enemy6))
|
||||
{
|
||||
if (def.defaultDuration <= 0f) enemy6.ModifyAttack(-delta);
|
||||
if (def.defaultDuration <= 0f)
|
||||
{
|
||||
enemy6.ModifyAttack(-delta);
|
||||
iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy6, PlayerBudeffIconType.ot_atk_down, -delta, 0f);
|
||||
}
|
||||
else if (!TryApplyRefreshOnlyTimedEffect(def, t, amountTotal, def.defaultDuration))
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(enemy6, -delta, def.defaultDuration));
|
||||
{
|
||||
var iconId = iBudeffPrefabController.Instance?.RegisterEnemyTimedEffect(enemy6, PlayerBudeffIconType.ot_atk_down, -delta, def.defaultDuration);
|
||||
StartCoroutine(ApplyTemporaryAttackChangeDirect(enemy6, -delta, def.defaultDuration, iconId, enemy6.GetInstanceID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1419,6 +1644,24 @@ ResolvedGroup:
|
||||
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>");
|
||||
}
|
||||
|
||||
if (ShouldSpawnApprForEffect(def.effectType))
|
||||
{
|
||||
var apprSprite = iBudeffPrefabController.Instance != null ? iBudeffPrefabController.Instance.GetSpriteForEffect(def.effectType) : null;
|
||||
if (apprSprite != null)
|
||||
{
|
||||
var apprTargets = ResolveTargetsLocal(def.defaultSelector, caster, specificTarget);
|
||||
if (apprTargets != null)
|
||||
{
|
||||
foreach (var t in apprTargets)
|
||||
{
|
||||
if (t == null) continue;
|
||||
var ally = t.GetComponent<AllyCombatant>() ?? t.GetComponentInChildren<AllyCombatant>(true);
|
||||
if (ally != null) iBudeffPrefabController.Instance?.SpawnApprForSlot(ally.slotIndex, apprSprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (def.defaultDuration > 0f)
|
||||
{
|
||||
var resolvedTargets = ResolveTargetsLocal(def.defaultSelector, caster, specificTarget);
|
||||
@@ -1984,61 +2227,98 @@ ResolvedGroup:
|
||||
ic?.RemoveBuff(buffId);
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryScoreEfficiencyChangeDirect(AllyCombatant ally, float delta, float duration)
|
||||
private IEnumerator ApplyTemporaryScoreEfficiencyChangeDirect(AllyCombatant ally, float delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - delta);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency + delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.scoreEfficiency = Mathf.Max(0f, ally.scoreEfficiency - delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryAttackChangeDirect(AllyCombatant ally, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryAttackChangeDirect(AllyCombatant ally, int delta, float duration, string iconId)
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(-delta);
|
||||
try
|
||||
{
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (ally == null) yield break;
|
||||
ally.ModifyAttack(-delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null && !string.IsNullOrEmpty(iconId))
|
||||
iBudeffPrefabController.Instance?.UnregisterTimedEffect(ally.slotIndex, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ApplyTemporaryAttackChangeDirect(EnemyCombatant enemy, int delta, float duration)
|
||||
private IEnumerator ApplyTemporaryAttackChangeDirect(EnemyCombatant enemy, int delta, float duration, string iconId, int enemyInstanceId)
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(delta);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(-delta);
|
||||
try
|
||||
{
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(delta);
|
||||
iBudeffPrefabController.Instance?.RefreshEnemyNow(enemy);
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (enemy == null) yield break;
|
||||
enemy.ModifyAttack(-delta);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconId) && enemyInstanceId != 0)
|
||||
iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutine helpers for direct DoT/HoT on GameObject targets
|
||||
private IEnumerator ApplyDamageOverTimeDirect(GameObject target, float totalAmount, float duration, float tickInterval, GameObject source)
|
||||
private IEnumerator ApplyDamageOverTimeDirect(GameObject target, float totalAmount, float duration, float tickInterval, GameObject source, string iconId, int allySlotIndex, int enemyInstanceId)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
var ic = target.GetComponent<ICombatant>();
|
||||
var ally = target.GetComponent<AllyCombatant>();
|
||||
if (ally == null && ic == null)
|
||||
try
|
||||
{
|
||||
Debug.LogWarning($"ApplyDamageOverTimeDirect: target {target.name} cannot receive damage");
|
||||
yield break;
|
||||
}
|
||||
if (target == null) yield break;
|
||||
var ic = target.GetComponent<ICombatant>();
|
||||
var ally = target.GetComponent<AllyCombatant>();
|
||||
if (ally == null && ic == null)
|
||||
{
|
||||
Debug.LogWarning($"ApplyDamageOverTimeDirect: target {target.name} cannot receive damage");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (duration <= 0f || tickInterval <= 0f)
|
||||
{
|
||||
if (ally != null) ally.ModifyHP(-Mathf.CeilToInt(totalAmount), true);
|
||||
else ic?.ReceiveDamage(totalAmount, source);
|
||||
yield break;
|
||||
}
|
||||
if (duration <= 0f || tickInterval <= 0f)
|
||||
{
|
||||
if (ally != null) ally.ModifyHP(-Mathf.CeilToInt(totalAmount), true);
|
||||
else ic?.ReceiveDamage(totalAmount, source);
|
||||
yield break;
|
||||
}
|
||||
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = totalAmount / ticks;
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
int ticks = Mathf.Max(1, Mathf.CeilToInt(duration / tickInterval));
|
||||
float perTick = totalAmount / ticks;
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
if (ally != null) ally.ModifyHP(-Mathf.CeilToInt(perTick), true);
|
||||
else ic?.ReceiveDamage(perTick, source);
|
||||
yield return new WaitForSeconds(tickInterval);
|
||||
elapsed += tickInterval;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ally != null) ally.ModifyHP(-Mathf.CeilToInt(perTick), true);
|
||||
else ic?.ReceiveDamage(perTick, source);
|
||||
yield return new WaitForSeconds(tickInterval);
|
||||
elapsed += tickInterval;
|
||||
if (!string.IsNullOrEmpty(iconId))
|
||||
{
|
||||
if (allySlotIndex >= 0) iBudeffPrefabController.Instance?.UnregisterTimedEffect(allySlotIndex, iconId);
|
||||
else if (enemyInstanceId != 0) iBudeffPrefabController.Instance?.UnregisterEnemyTimedEffect(enemyInstanceId, iconId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user