132 lines
5.3 KiB
C#
132 lines
5.3 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using BaseGames.Player;
|
||
using BaseGames.Input;
|
||
using BaseGames.Feedback;
|
||
using BaseGames.Core;
|
||
|
||
namespace BaseGames.Spells
|
||
{
|
||
/// <summary>
|
||
/// 法术管理器(架构 BaseGames.Spells)。
|
||
/// 挂在 Player 上,管理已装备法术的施放、冷却与资源消耗。
|
||
/// 装备/卸下法术通过 EquipSpell / UnequipSpell 公共 API 完成(如护符系统调用)。
|
||
///
|
||
/// 输入事件:订阅 InputReaderSO.SpellCastEvent(对应 InputActionAsset 中的 "Spell" Action)。
|
||
/// </summary>
|
||
public class SpellManager : MonoBehaviour, ISpellService
|
||
{
|
||
[Header("依赖引用")]
|
||
[SerializeField] private PlayerStats _stats;
|
||
[SerializeField] private InputReaderSO _input;
|
||
|
||
[Header("法术挂载点")]
|
||
[Tooltip("法术 HitBox / 投射物的生成位置")]
|
||
[SerializeField] private Transform _spellSocket;
|
||
|
||
// 当前装备的法术(单槽;如需多槽可扩展为数组)
|
||
private SpellSO _equippedSpell;
|
||
private float _cooldownRemaining;
|
||
private IFeedbackPlayer _feedback;
|
||
|
||
// ── ISpellService 事件 ────────────────────────────────────────────────
|
||
/// <inheritdoc/>
|
||
public event Action<SpellSO> OnSpellChanged;
|
||
|
||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||
|
||
private void Awake()
|
||
{
|
||
_feedback = GetComponentInChildren<IFeedbackPlayer>()
|
||
?? GetComponentInParent<IFeedbackPlayer>()
|
||
?? NullFeedbackPlayer.Instance;
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
ServiceLocator.Register<ISpellService>(this);
|
||
if (_input != null)
|
||
_input.SpellCastEvent += TryCastSpell;
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
ServiceLocator.Unregister<ISpellService>(this);
|
||
if (_input != null)
|
||
_input.SpellCastEvent -= TryCastSpell;
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (_cooldownRemaining > 0f)
|
||
_cooldownRemaining -= Time.deltaTime;
|
||
}
|
||
|
||
// ── 公共 API ─────────────────────────────────────────────────────────
|
||
|
||
/// <summary>装备一个法术(替换当前已装备的)。</summary>
|
||
public void EquipSpell(SpellSO spell)
|
||
{
|
||
_equippedSpell = spell;
|
||
_cooldownRemaining = 0f;
|
||
OnSpellChanged?.Invoke(spell);
|
||
}
|
||
|
||
/// <summary>卸下当前装备的法术。</summary>
|
||
public void UnequipSpell()
|
||
{
|
||
_equippedSpell = null;
|
||
_cooldownRemaining = 0f;
|
||
OnSpellChanged?.Invoke(null);
|
||
}
|
||
|
||
/// <summary>返回当前冷却进度(0 = 就绪,1 = 刚施放)。供 UI 血条使用。</summary>
|
||
public float CooldownFraction
|
||
=> _equippedSpell != null && _equippedSpell.cooldown > 0f
|
||
? Mathf.Clamp01(_cooldownRemaining / _equippedSpell.cooldown)
|
||
: 0f;
|
||
|
||
public SpellSO EquippedSpell => _equippedSpell;
|
||
public bool IsReady => _equippedSpell != null && _cooldownRemaining <= 0f;
|
||
|
||
// ── 内部施放逻辑 ──────────────────────────────────────────────────────
|
||
|
||
private void TryCastSpell()
|
||
{
|
||
if (_equippedSpell == null || _cooldownRemaining > 0f) return;
|
||
if (_stats == null) return;
|
||
|
||
// 消耗资源(扣除护符提供的灵力减免后计算实际消耗)
|
||
int cost = Mathf.Max(0, _equippedSpell.baseCost - _stats.SoulCostReduction);
|
||
bool consumed = _equippedSpell.resourceType == SpellResourceType.SoulPower
|
||
? _stats.ConsumeSoulPower(cost)
|
||
: _stats.ConsumeSpiritPower(cost);
|
||
if (!consumed) return;
|
||
|
||
_cooldownRemaining = _equippedSpell.cooldown;
|
||
|
||
// 施放反馈
|
||
_feedback.TriggerPreset("spell_cast");
|
||
|
||
ExecuteSpellEffect(_equippedSpell);
|
||
}
|
||
|
||
private void ExecuteSpellEffect(SpellSO spell)
|
||
{
|
||
// 生成 HitBox Prefab(近战/爆炸类法术)
|
||
if (spell.spellHitBoxPrefab != null)
|
||
{
|
||
var socket = _spellSocket != null ? _spellSocket : transform;
|
||
Instantiate(spell.spellHitBoxPrefab, socket.position, socket.rotation, socket);
|
||
}
|
||
|
||
// TODO: 根据 spell.effectType 扩展施放逻辑
|
||
// SpellEffectType.Projectile → ProjectileManager 发射
|
||
// SpellEffectType.AreaOfEffect → AreaOfEffectSpawner 生成
|
||
// SpellEffectType.SelfBuff → PlayerStats/StatusEffectManager 施加增益
|
||
// SpellEffectType.SummonShade → 召唤投影 Prefab
|
||
// SpellEffectType.TeleportBlink → 瞬移至光标/方向
|
||
}
|
||
}
|
||
}
|