Files
zeling_v2/Assets/_Game/Scripts/Skills/SkillModifierRegistry.cs

168 lines
7.0 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 System.Collections.Generic;
using UnityEngine;
using Animancer;
using BaseGames.Player;
namespace BaseGames.Skills
{
/// <summary>
/// 技能数值修改维度(架构 09_ProgressionModule §10
/// </summary>
public enum SkillStat { Damage, Cost, Cooldown, Range, Duration }
/// <summary>
/// 插槽覆盖描述符:某护符将指定形态的某个技能槽替换为另一技能。
/// </summary>
[Serializable]
public struct SkillSlotOverride
{
public FormSO targetForm; // null = 所有形态
public string targetSlot; // 使用 SkillSlotNames 中的常量SoulSkill / SpiritSkill1 / SpiritSkill2
public FormSkillSO replacementSkill; // 替换目标技能
public int priority; // 高优先级覆盖低优先级
}
/// <summary>
/// 所有数值修改器叠加后的运行时参数快照(架构 09_ProgressionModule §10
/// 由 SkillModifierRegistry.GetEffectiveParams() 生成,传入 SkillManager 使用。
/// </summary>
public struct EffectiveSkillParams
{
public FormSkillSO baseSkill; // 原始 SO 引用(不变)
public int effectiveCost; // 修改后消耗
public float effectiveCooldown; // 修改后冷却(秒)
public float damageMult; // 伤害倍率1.0 = 无增益)
public float rangeMult; // 范围倍率
public FeedbackPresetSO effectiveFeedback; // 最终特效预设null = 回退原始)
public ClipTransition effectiveAnimation; // 最终施法动画null = 回退原始)
/// <summary>以技能 SO 默认值初始化,无任何修改器加成。</summary>
public static EffectiveSkillParams FromBase(FormSkillSO skill) => new()
{
baseSkill = skill,
effectiveCost = skill.baseCost,
effectiveCooldown = skill.cooldown,
damageMult = 1f,
rangeMult = 1f,
effectiveFeedback = null,
effectiveAnimation = default,
};
}
// 内部记录结构,存储单条数值修改
internal struct SkillStatEntry
{
public SkillStat stat;
public float delta;
public bool isPercent;
}
/// <summary>
/// 技能修改器注册表(架构 09_ProgressionModule §10
/// 挂在 Player 上,收集所有护符对技能数值的修改。
/// SkillManager 在施放技能时调用 GetEffectiveParams() 获取最终参数。
/// </summary>
public class SkillModifierRegistry : MonoBehaviour
{
// skillId → 一组数值修改
private readonly Dictionary<string, List<SkillStatEntry>> _modifiers = new();
// 插槽覆盖列表(按 priority 降序排列)
private readonly List<SkillSlotOverride> _slotOverrides = new();
// ── 数值修改 ────────────────────────────────────────────────────────
public void Register(string skillId, SkillStat stat, float delta, bool isPercent)
{
if (!_modifiers.TryGetValue(skillId, out var list))
{
list = new List<SkillStatEntry>();
_modifiers[skillId] = list;
}
list.Add(new SkillStatEntry { stat = stat, delta = delta, isPercent = isPercent });
}
public void Unregister(string skillId, SkillStat stat, float delta, bool isPercent)
{
if (!_modifiers.TryGetValue(skillId, out var list)) return;
list.RemoveAll(e => e.stat == stat &&
Mathf.Approximately(e.delta, delta) &&
e.isPercent == isPercent);
}
/// <summary>
/// 对给定技能叠加所有已注册修改器,返回一次性快照供 SkillManager 使用。
/// </summary>
public EffectiveSkillParams GetEffectiveParams(FormSkillSO skill)
{
var p = EffectiveSkillParams.FromBase(skill);
if (!_modifiers.TryGetValue(skill.skillId, out var entries)) return p;
float flatCost = 0, pctCost = 1f;
float flatCooldown = 0, pctCooldown = 1f;
float flatDamage = 0, pctDamage = 1f;
float flatRange = 0, pctRange = 1f;
foreach (var e in entries)
{
switch (e.stat)
{
case SkillStat.Cost:
if (e.isPercent) pctCost += e.delta;
else flatCost += e.delta;
break;
case SkillStat.Cooldown:
if (e.isPercent) pctCooldown += e.delta;
else flatCooldown += e.delta;
break;
case SkillStat.Damage:
if (e.isPercent) pctDamage += e.delta;
else flatDamage += e.delta;
break;
case SkillStat.Range:
if (e.isPercent) pctRange += e.delta;
else flatRange += e.delta;
break;
}
}
p.effectiveCost = Mathf.Max(0, Mathf.RoundToInt(skill.baseCost * pctCost + flatCost));
p.effectiveCooldown = Mathf.Max(0, skill.cooldown * pctCooldown + flatCooldown);
p.damageMult = 1f + (pctDamage - 1f) + flatDamage;
p.rangeMult = 1f + (pctRange - 1f) + flatRange;
return p;
}
// ── 插槽覆盖 ────────────────────────────────────────────────────────
public void AddSlotOverride(SkillSlotOverride data)
{
_slotOverrides.Add(data);
_slotOverrides.Sort((a, b) => b.priority.CompareTo(a.priority));
}
public void RemoveSlotOverride(SkillSlotOverride data)
{
_slotOverrides.RemoveAll(o =>
o.targetForm == data.targetForm &&
o.targetSlot == data.targetSlot &&
o.replacementSkill == data.replacementSkill);
}
/// <summary>
/// 获取当前形态某槽位的实际技能(考虑插槽覆盖)。
/// 没有覆盖时返回 null调用方保留原始技能
/// </summary>
public FormSkillSO GetOverriddenSkill(FormSO form, string slotName)
{
foreach (var o in _slotOverrides)
{
bool formMatch = o.targetForm == null || o.targetForm == form;
if (formMatch && o.targetSlot == slotName)
return o.replacementSkill;
}
return null;
}
}
}