174 lines
7.4 KiB
C#
174 lines
7.4 KiB
C#
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); // 自动标记脏并重新钳制
|
|
}
|
|
}
|
|
}
|