using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BaseGames.Combat;
namespace BaseGames.Enemies.Abilities
{
///
/// 近战连击能力。
/// 按 EnemyAbilitySO.attackSequence 顺序播放动画段,每段在 hitBoxEnterT~hitBoxExitT
/// 之间激活对应槽位的 HitBox。
///
/// 设计:HitBox 通过命名槽位绑定,避免对 EnemyCombat 的硬依赖;同一敌人多个近战
/// 能力可共享同一 HitBox 槽位(如不同段使用同一把武器)。
///
public sealed class MeleeAttackAbility : EnemyAbilityBase
{
[System.Serializable]
public struct HitBoxSlot
{
public string slotName;
public HitBox hitBox;
}
[Header("HitBox 槽位(按名字索引)")]
[SerializeField] private HitBoxSlot[] _hitBoxSlots;
[Header("行为")]
[SerializeField] private bool _faceTargetOnStart = true;
private Dictionary _slotMap;
protected override void Awake()
{
base.Awake();
_slotMap = new Dictionary(_hitBoxSlots?.Length ?? 0);
if (_hitBoxSlots != null)
{
for (int i = 0; i < _hitBoxSlots.Length; i++)
{
var s = _hitBoxSlots[i];
if (s.hitBox != null && !string.IsNullOrEmpty(s.slotName))
_slotMap[s.slotName] = s.hitBox;
}
}
}
protected override IEnumerator ExecuteCoroutine()
{
var seq = _config != null ? _config.attackSequence : null;
if (seq == null || seq.Length == 0) yield break;
if (_faceTargetOnStart && _enemy != null && _enemy.PlayerTransform != null)
FaceTarget(_enemy.PlayerTransform);
for (int i = 0; i < seq.Length; i++)
{
var atk = seq[i];
if (atk == null) continue;
yield return PlayAttackStep(atk);
if (atk.postDelay > 0f)
yield return EnemyAbilityWaits.Get(atk.postDelay);
}
}
private IEnumerator PlayAttackStep(EnemyAttackSO atk)
{
Phase = AbilityRunState.Active;
float duration = atk.fallbackDuration;
if (atk.clip != null && _animancer != null)
{
var state = _animancer.Play(atk.clip);
if (state != null && state.Length > 0f) duration = state.Length;
}
HitBox hb = null;
if (!string.IsNullOrEmpty(atk.hitBoxSlot))
_slotMap.TryGetValue(atk.hitBoxSlot, out hb);
float enterAbs = atk.hitBoxEnterT * duration;
float exitAbs = atk.hitBoxExitT * duration;
float t = 0f;
bool active = false;
while (t < duration)
{
t += Time.deltaTime;
if (hb != null)
{
if (!active && t >= enterAbs)
{
hb.Activate(atk.damageSource, _transform);
active = true;
}
else if (active && t >= exitAbs)
{
hb.Deactivate();
active = false;
}
}
yield return null;
}
if (active && hb != null) hb.Deactivate();
}
protected override void OnInterrupted(InterruptReason reason)
{
// 确保 HitBox 被关闭,防止中断时残留激活
if (_hitBoxSlots == null) return;
for (int i = 0; i < _hitBoxSlots.Length; i++)
{
var hb = _hitBoxSlots[i].hitBox;
if (hb != null && hb.IsActive) hb.Deactivate();
}
}
}
}