gameplay所有基本功能都装了,加入了全局警告。加入飘字等各种东西。

This commit is contained in:
FloatGaming
2026-02-21 00:20:03 +08:00
parent 9f7c1c57b7
commit 14fb281d6f
254 changed files with 102927 additions and 9427 deletions
+348 -68
View File
@@ -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);
}
}
}