using UnityEngine;
using Animancer;
using System.Collections.Generic;
using BaseGames.Player;
using BaseGames.Input;
using BaseGames.Combat;
namespace BaseGames.Skills
{
///
/// 技能管理器(架构 09_ProgressionModule §9)。
/// 挂在 Player 上,订阅输入事件并处理所有技能施放逻辑。
/// 通过 UpdateSkillSet() 由 FormController 注入当前形态技能。
/// 直接使用 AnimancerComponent 播放动画,无需 SoulSkillState FSM。
///
public class SkillManager : MonoBehaviour
{
[Header("依赖引用")]
[SerializeField] private PlayerStats _stats;
[SerializeField] private AnimancerComponent _animancer;
[SerializeField] private InputReaderSO _input;
[SerializeField] private SkillModifierRegistry _modifiers;
[Header("技能挂载点")]
[SerializeField] private Transform _skillSocket; // [SkillSocket] 子节点
// 当前形态技能集(绑定到对应输入槽)
private FormSkillSO _soulSkill;
private FormSkillSO _spirit1;
private FormSkillSO _spirit2;
// 冷却字典(FormSkillSO → 剩余冷却秒数),UpdateSkillSet 时重建
private readonly Dictionary _cooldowns = new(3);
// 无分配 Update 遍历用的快照数组
private FormSkillSO[] _activeSkills = System.Array.Empty();
// ── 生命周期 ──────────────────────────────────────────────────────────
private void OnEnable()
{
if (_input == null) return;
_input.SoulSkillEvent += TrySoulSkill;
_input.SpiritSkill1StartedEvent += TrySpiritSkill1;
_input.SpiritSkill2StartedEvent += TrySpiritSkill2;
}
private void OnDisable()
{
if (_input == null) return;
_input.SoulSkillEvent -= TrySoulSkill;
_input.SpiritSkill1StartedEvent -= TrySpiritSkill1;
_input.SpiritSkill2StartedEvent -= TrySpiritSkill2;
}
private void Update()
{
for (int i = 0; i < _activeSkills.Length; i++)
{
var s = _activeSkills[i];
if (_cooldowns.TryGetValue(s, out float cd) && cd > 0f)
_cooldowns[s] = cd - Time.deltaTime;
}
}
// ── 公共 API ─────────────────────────────────────────────────────────
/// 切换形态时由 FormController 调用,注入当前形态的三个技能。
public void UpdateSkillSet(FormSkillSO soul, FormSkillSO spirit1, FormSkillSO spirit2)
{
_soulSkill = soul;
_spirit1 = spirit1;
_spirit2 = spirit2;
_cooldowns.Clear();
// 构建无分配遍历快照(固定大小数组,避免 List + ToArray GC)
int count = (soul != null ? 1 : 0) + (spirit1 != null ? 1 : 0) + (spirit2 != null ? 1 : 0);
if (_activeSkills.Length != count)
_activeSkills = count > 0 ? new FormSkillSO[count] : System.Array.Empty();
int idx = 0;
if (soul != null) { _cooldowns[soul] = 0f; _activeSkills[idx++] = soul; }
if (spirit1 != null) { _cooldowns[spirit1] = 0f; _activeSkills[idx++] = spirit1; }
if (spirit2 != null) { _cooldowns[spirit2] = 0f; _activeSkills[idx] = spirit2; }
}
// ── 内部施放逻辑 ─────────────────────────────────────────────────────
private void TrySoulSkill() => TryCastSkill(_soulSkill);
private void TrySpiritSkill1() => TryCastSkill(_spirit1);
private void TrySpiritSkill2() => TryCastSkill(_spirit2);
private void TryCastSkill(FormSkillSO skill)
{
if (skill == null) return;
var p = _modifiers != null
? _modifiers.GetEffectiveParams(skill)
: EffectiveSkillParams.FromBase(skill);
if (!_cooldowns.TryGetValue(skill, out float cooldown) || cooldown > 0f) return;
// 消耗资源
bool consumed = skill.resourceType == SkillResourceType.SoulPower
? _stats.ConsumeSoulPower(p.effectiveCost)
: _stats.ConsumeSpiritPower(p.effectiveCost);
if (!consumed) return;
_cooldowns[skill] = p.effectiveCooldown;
// 播放动画(优先修改器动画,回退技能默认动画)
var clip = p.effectiveAnimation.Clip != null
? p.effectiveAnimation
: skill.castAnimation;
if (clip.Clip != null && _animancer != null)
_animancer.Play(clip);
// 生成 HitBox Prefab(近战/爆炸类技能)
if (skill.SkillHitBoxPrefab != null)
{
var socket = _skillSocket != null ? _skillSocket : transform;
var go = Object.Instantiate(skill.SkillHitBoxPrefab, socket.position,
socket.rotation, socket);
var inst = go.GetComponent();
inst?.Activate(skill.damageSource, transform);
inst?.AutoDestroyAfter(skill.castLockDuration > 0f ? skill.castLockDuration : 0.5f);
}
}
// ── 属性查询 ─────────────────────────────────────────────────────────
public FormSkillSO SoulSkill => _soulSkill;
public FormSkillSO Spirit1 => _spirit1;
public FormSkillSO Spirit2 => _spirit2;
public float SoulCooldownRatio =>
(_soulSkill != null && _soulSkill.cooldown > 0 &&
_cooldowns.TryGetValue(_soulSkill, out float cd))
? Mathf.Clamp01(cd / _soulSkill.cooldown) : 0f;
}
}