Files
bansonic_beta_main/Assets/scripts/Combat/EnemyCombatant.cs
T

196 lines
6.1 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
// EnemyCombatant 是单个场上敌人的运行时表现(所有敌人共用同一个 GameObject,按顺序登场)
// 实现 ICombatant:被 EffectSystem/SkillBuilder 调用
public class EnemyCombatant : MonoBehaviour, ICombatant
{
public EnemyData_SO sourceData;
// runtime stats
public int maxHP = 100;
public int currentHP = 100;
public int maxMana = 0;
public int currentMana = 0;
public float damageResistance = 0f;
public int attack = 0;
private List<Buff> activeBuffs = new List<Buff>();
// Events to notify manager/UI
public event Action<EnemyCombatant> OnEnemyDied;
public event Action<EnemyCombatant> OnEnemyRevived;
private bool isDead = false;
private void Awake()
{
// Ensure consistent name so EffectSystem can find it
gameObject.name = "thisEnemy";
}
// 初始化/复活为指定的 EnemyData_SO
public void InitializeFromSO(EnemyData_SO so)
{
sourceData = so;
if (so != null)
{
maxHP = Mathf.Max(1, so.enemy_maxHP);
damageResistance = so.enemy_damageResistance;
attack = so.enemy_baseAttack;
maxMana = Mathf.Max(0, so.GetEffectiveMaxMana());
}
else
{
maxHP = 100;
damageResistance = 0f;
attack = 0;
maxMana = 0;
}
// revive
currentHP = maxHP;
currentMana = 0;
isDead = false;
// notify listeners (UI/manager) that enemy revived
OnEnemyRevived?.Invoke(this);
// Trigger primary skill on revive if defined and trigger condition matches
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnEnemyRevive);
}
// 尝试触发 primary skill,当触发条件匹配时调用 SkillBuilder
private void TryTriggerPrimarySkill(SkillDefinition.SkillTrigger when)
{
if (sourceData == null) return;
var def = sourceData.GetPrimarySkill();
if (def == null) return;
if (def.triggerCondition != when) return;
// Evaluate formula into amount using enemy fields as variables
float perTick = 0f;
var vars = new Dictionary<string, float>();
vars["maxHP"] = maxHP;
vars["maxMana"] = maxMana;
vars["damageResistance"] = damageResistance;
vars["attack"] = attack;
vars["enemy_currentHP"] = currentHP;
if (!string.IsNullOrWhiteSpace(def.formula))
{
if (!SkillDefinition.TryEvaluateFormula(def.formula, vars, out float result))
perTick = 0f;
else
perTick = result;
}
float totalAmount = perTick;
if (!def.IsSingleInstance)
{
float tickInterval = def.GetEffectiveTickInterval();
int ticks = Mathf.Max(1, Mathf.CeilToInt(def.defaultDuration / tickInterval));
totalAmount = perTick * ticks;
}
// Use SkillBuilder to execute effect, using this Enemy GameObject as caster
if (SkillBuilder.Instance != null)
{
SkillBuilder.Instance.ExecuteSkill(def.displayName ?? def.skillId, def.effectType, totalAmount, def.defaultSelector, this.gameObject, null, def.defaultDuration, def.defaultTickInterval);
}
}
public void ReceiveDamage(float amount, GameObject source)
{
if (isDead) return;
float effective = amount * (1f - damageResistance);
int delta = Mathf.CeilToInt(effective);
currentHP = Mathf.Clamp(currentHP - delta, 0, maxHP);
// grant mana based on sourceData setting
if (sourceData != null && sourceData.manaGainOnDamagePercent > 0f && maxMana > 0)
{
float manaGainRaw = effective * sourceData.manaGainOnDamagePercent;
int manaGain = Mathf.CeilToInt(manaGainRaw);
ModifyMana(manaGain, true);
}
if (currentHP <= 0)
{
Die();
}
}
public void ReceiveHeal(float amount, GameObject source)
{
if (isDead) return;
int delta = Mathf.CeilToInt(amount);
currentHP = Mathf.Clamp(currentHP + delta, 0, maxHP);
}
public void ApplyBuff(Buff buff, GameObject source)
{
if (buff == null) return;
activeBuffs.Add(buff);
// Simple apply: only modify healReceivedMultiplier or attackMultiplier as example
if (buff.attackMultiplier != 1f) attack = Mathf.RoundToInt(attack * buff.attackMultiplier);
}
public void RemoveBuff(string buffId)
{
var b = activeBuffs.Find(x => x.buffId == buffId);
if (b != null)
{
activeBuffs.Remove(b);
}
}
public void ModifyHP(int delta)
{
if (isDead && delta > 0) return;
currentHP = Mathf.Clamp(currentHP + delta, 0, maxHP);
if (currentHP <= 0) Die();
}
public void ModifyMana(int delta, bool animate = true)
{
if (maxMana <= 0) return;
int old = currentMana;
currentMana = Mathf.Clamp(currentMana + delta, 0, maxMana);
// If mana reached full, trigger primary skill and reset mana
if (currentMana >= maxMana && maxMana > 0 && Application.isPlaying)
{
Debug.Log($"[EnemyCombatant] Mana full for {gameObject.name} (slot) - triggering OnManaFull");
try
{
// Trigger primary skill configured for OnManaFull
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnManaFull);
}
catch (Exception ex)
{
Debug.LogError($"[EnemyCombatant] TryTriggerPrimarySkill threw: {ex}");
}
// reset mana after casting
currentMana = 0;
}
}
private void Die()
{
if (isDead) return;
isDead = true;
// notify listeners before possibly being reused
OnEnemyDied?.Invoke(this);
// Trigger primary skill on death if configured
TryTriggerPrimarySkill(SkillDefinition.SkillTrigger.OnEnemyDead);
}
// Utility: expose a read-only status
public bool IsDead => isDead;
}