引入属性、伤害管线
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using SepCore.Battle;
|
||||
using SepCore.Definition;
|
||||
|
||||
namespace VampireLike.Tests.Gameplay.EditMode
|
||||
{
|
||||
[TestFixture]
|
||||
public class AttributePipelineTests
|
||||
{
|
||||
[Test]
|
||||
public void FormulaOrder_CalculatesFlatPercentAddAndPercentMultCorrectly()
|
||||
{
|
||||
var stat = new CharacterStat(StatType.MeleeDamage, baseValue: 10f);
|
||||
|
||||
// Formula: ((Base + Sum(Flat)) * (1 + Sum(PercentAdd))) * Prod(PercentMult)
|
||||
// Flat: +5, +3 -> Sum = +8 -> Base + Flat = 18
|
||||
stat.AddModifier(new StatModifier(5f, ModifierType.Flat));
|
||||
stat.AddModifier(new StatModifier(3f, ModifierType.Flat));
|
||||
|
||||
// PercentAdd: +20% (0.2), +30% (0.3) -> Sum = +50% -> 1 + 0.5 = 1.5
|
||||
stat.AddModifier(new StatModifier(0.2f, ModifierType.PercentAdd));
|
||||
stat.AddModifier(new StatModifier(0.3f, ModifierType.PercentAdd));
|
||||
|
||||
// PercentMult: 1.2x, 1.5x -> Prod = 1.8
|
||||
stat.AddModifier(new StatModifier(1.2f, ModifierType.PercentMult));
|
||||
stat.AddModifier(new StatModifier(1.5f, ModifierType.PercentMult));
|
||||
|
||||
// Expected: 18 * 1.5 * 1.8 = 48.6
|
||||
Assert.AreEqual(48.6f, stat.Value, 1e-4f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DirtyFlag_CachesValueUntilModified()
|
||||
{
|
||||
var stat = new CharacterStat(StatType.MaxHp, baseValue: 100f);
|
||||
var mod = new StatModifier(20f, ModifierType.Flat);
|
||||
|
||||
stat.AddModifier(mod);
|
||||
float firstRead = stat.Value;
|
||||
Assert.AreEqual(120f, firstRead, 1e-4f);
|
||||
|
||||
// 读取多次,验证缓存值一致
|
||||
float secondRead = stat.Value;
|
||||
Assert.AreEqual(120f, secondRead, 1e-4f);
|
||||
|
||||
// 移除修饰器后脏标记生效,值重新计算
|
||||
stat.RemoveModifier(mod);
|
||||
Assert.AreEqual(100f, stat.Value, 1e-4f);
|
||||
|
||||
// 修改 BaseValue 后脏标记生效
|
||||
stat.BaseValue = 150f;
|
||||
Assert.AreEqual(150f, stat.Value, 1e-4f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveAllModifiersFromSource_RemovesOnlyMatchingSource()
|
||||
{
|
||||
var pipeline = new AttributePipeline();
|
||||
var sourceA = new object();
|
||||
var sourceB = new object();
|
||||
|
||||
pipeline.SetBaseValue(StatType.MeleeDamage, 10f);
|
||||
pipeline.SetBaseValue(StatType.MoveSpeed, 5f);
|
||||
|
||||
// sourceA 附加加成到近战伤害与移速
|
||||
pipeline.AddModifier(StatType.MeleeDamage, new StatModifier(10f, ModifierType.Flat, sourceA));
|
||||
pipeline.AddModifier(StatType.MoveSpeed, new StatModifier(0.2f, ModifierType.PercentAdd, sourceA));
|
||||
|
||||
// sourceB 附加加成到近战伤害
|
||||
pipeline.AddModifier(StatType.MeleeDamage, new StatModifier(5f, ModifierType.Flat, sourceB));
|
||||
|
||||
Assert.AreEqual(25f, pipeline.GetValue(StatType.MeleeDamage), 1e-4f);
|
||||
Assert.AreEqual(6f, pipeline.GetValue(StatType.MoveSpeed), 1e-4f);
|
||||
|
||||
// 清理 sourceA
|
||||
int removedCount = pipeline.RemoveAllModifiersFromSource(sourceA);
|
||||
Assert.AreEqual(2, removedCount);
|
||||
|
||||
// 验证 sourceA 被清除,sourceB 依然生效
|
||||
Assert.AreEqual(15f, pipeline.GetValue(StatType.MeleeDamage), 1e-4f);
|
||||
Assert.AreEqual(5f, pipeline.GetValue(StatType.MoveSpeed), 1e-4f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatConfig_ClampsMinAndMaxCorrectly()
|
||||
{
|
||||
// 闪避率硬上限 80% (0.8f) 来自配置表
|
||||
var dodgeConfig = new StatConfig(StatType.Dodge, hasMin: true, minValue: 0f, hasMax: true, maxValue: 0.80f);
|
||||
var dodgeStat = new CharacterStat(StatType.Dodge, baseValue: 0.50f, dodgeConfig);
|
||||
dodgeStat.AddModifier(new StatModifier(0.40f, ModifierType.Flat));
|
||||
Assert.AreEqual(0.80f, dodgeStat.Value, 1e-4f);
|
||||
|
||||
// 移速保底 0.5f 来自配置表
|
||||
var speedConfig = new StatConfig(StatType.MoveSpeed, hasMin: true, minValue: 0.5f);
|
||||
var speedStat = new CharacterStat(StatType.MoveSpeed, baseValue: 3f, speedConfig);
|
||||
speedStat.AddModifier(new StatModifier(-10f, ModifierType.Flat));
|
||||
Assert.AreEqual(0.5f, speedStat.Value, 1e-4f);
|
||||
|
||||
// 暴击率硬上限 90% (0.9f) 来自配置表
|
||||
var critConfig = new StatConfig(StatType.CritChance, hasMin: true, minValue: 0f, hasMax: true, maxValue: 0.90f);
|
||||
var critStat = new CharacterStat(StatType.CritChance, baseValue: 0.50f, critConfig);
|
||||
critStat.AddModifier(new StatModifier(0.60f, ModifierType.Flat));
|
||||
Assert.AreEqual(0.90f, critStat.Value, 1e-4f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FromLuban_ConvertsFlatAndPercentModifiersCorrectly()
|
||||
{
|
||||
// 1. 固定值 (Flat): 比如近战伤害 +15
|
||||
var flatConfig = new StatModifierConfig(StatType.MeleeDamage, 15f, ModifierType.Flat);
|
||||
var flatStatMod = StatModifier.FromLuban(flatConfig);
|
||||
Assert.AreEqual(ModifierType.Flat, flatStatMod.Type);
|
||||
Assert.AreEqual(15f, flatStatMod.Value, 1e-4f);
|
||||
|
||||
// 2. 百分比累加 (PercentAdd): 比如伤害 +20% (0.2f)
|
||||
var percentConfig = new StatModifierConfig(StatType.DamagePercent, 0.2f, ModifierType.PercentAdd);
|
||||
var percentStatMod = StatModifier.FromLuban(percentConfig);
|
||||
Assert.AreEqual(ModifierType.PercentAdd, percentStatMod.Type);
|
||||
Assert.AreEqual(0.2f, percentStatMod.Value, 1e-4f);
|
||||
|
||||
// 3. 独立乘区 (PercentMult): 比如伤害 1.5 倍
|
||||
var multConfig = new StatModifierConfig(StatType.DamagePercent, 1.5f, ModifierType.PercentMult);
|
||||
var multStatMod = StatModifier.FromLuban(multConfig);
|
||||
Assert.AreEqual(ModifierType.PercentMult, multStatMod.Type);
|
||||
Assert.AreEqual(1.5f, multStatMod.Value, 1e-4f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnStatChanged_FiresOnModifications()
|
||||
{
|
||||
var pipeline = new AttributePipeline();
|
||||
StatType changedStat = StatType.None;
|
||||
float changedValue = 0f;
|
||||
int eventCount = 0;
|
||||
|
||||
pipeline.OnStatChanged += (type, val) =>
|
||||
{
|
||||
changedStat = type;
|
||||
changedValue = val;
|
||||
eventCount++;
|
||||
};
|
||||
|
||||
// 设置基础值
|
||||
pipeline.SetBaseValue(StatType.Armor, 10f);
|
||||
Assert.AreEqual(StatType.Armor, changedStat);
|
||||
Assert.AreEqual(10f, changedValue, 1e-4f);
|
||||
Assert.AreEqual(1, eventCount);
|
||||
|
||||
// 添加修饰器
|
||||
var mod = new StatModifier(5f, ModifierType.Flat);
|
||||
pipeline.AddModifier(StatType.Armor, mod);
|
||||
Assert.AreEqual(15f, changedValue, 1e-4f);
|
||||
Assert.AreEqual(2, eventCount);
|
||||
|
||||
// 移除修饰器
|
||||
pipeline.RemoveModifier(StatType.Armor, mod);
|
||||
Assert.AreEqual(10f, changedValue, 1e-4f);
|
||||
Assert.AreEqual(3, eventCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DynamicConfig_MarksDirtyAndUpdatesClamp()
|
||||
{
|
||||
var stat = new CharacterStat(StatType.Dodge, baseValue: 0.90f);
|
||||
Assert.AreEqual(0.90f, stat.Value, 1e-4f); // 未注入配置时不钳制
|
||||
|
||||
// 动态注入配置,硬上限 80%
|
||||
stat.Config = new StatConfig(StatType.Dodge, hasMax: true, maxValue: 0.80f);
|
||||
Assert.AreEqual(0.80f, stat.Value, 1e-4f); // 自动标记脏并重新钳制
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user