Files
zeling_v2/Assets/_Game/Scripts/Spells/SpellManager.cs
2026-05-25 11:54:37 +08:00

132 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 → 瞬移至光标/方向
}
}
}