多轮审查和修复
This commit is contained in:
167
Assets/Scripts/Skills/SkillModifierRegistry.cs
Normal file
167
Assets/Scripts/Skills/SkillModifierRegistry.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user