Battle formulas: action values as stat formulas, costs from stat pools, MP gauge for party members, action schema
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
69390b7743
20 files changed
+724
No files matched your search
@@ -0,0 +1,101 @@
|
||||
namespace Cthangover.Mods.BattleFormulas.Tests
|
||||
{
|
||||
using Cthangover.Core.Characters;
|
||||
using Cthangover.Core.Mods.Testing;
|
||||
using Cthangover.Core.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// A formula action applied to two plain characters: damage through shields, heals
|
||||
/// capped at the maximum, refused actions changing nothing.
|
||||
/// </summary>
|
||||
public class FormulaActionExecutorTests
|
||||
{
|
||||
private static Character Fighter(string id, int hp, int attack = 0, int defence = 0, int points = 3)
|
||||
{
|
||||
var c = new Character { ID = id, Level = 1 };
|
||||
c.Attributes.Health.Init(hp);
|
||||
c.Attributes.Attack.Init(attack);
|
||||
c.Attributes.Defence.Init(defence);
|
||||
c.Attributes.Point.Init(points);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static ActionCharacter Action(ActionCharacterType type, params string[] values)
|
||||
{
|
||||
var props = new PropertyData();
|
||||
foreach (var value in values)
|
||||
{
|
||||
var eq = value.IndexOf('=');
|
||||
props.Values[value.Substring(0, eq)] = value.Substring(eq + 1);
|
||||
}
|
||||
return new ActionCharacter { ID = "test/formula", Type = type, Properties = props };
|
||||
}
|
||||
|
||||
[ModTest("executor/damage_from_live_attributes")]
|
||||
static void DamageFromLiveAttributes(ModTestContext ctx)
|
||||
{
|
||||
var hero = Fighter("test_hero_no_stats", 20, attack: 10);
|
||||
var slime = Fighter("test_slime_no_stats", 30);
|
||||
|
||||
var result = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToEnemy, "Formula=a.attack * 2 - 3"), hero, slime);
|
||||
|
||||
ModAssert.True(result.Result, "applied");
|
||||
ModAssert.Equal(17, result.Target.Damage, "10 * 2 - 3");
|
||||
ModAssert.Equal(13, slime.Attributes.Health.Value, "health lost");
|
||||
}
|
||||
|
||||
[ModTest("executor/shield_absorbs_unless_pierce")]
|
||||
static void ShieldAbsorbsUnlessPierce(ModTestContext ctx)
|
||||
{
|
||||
var hero = Fighter("test_hero_no_stats", 20);
|
||||
var guard = Fighter("test_guard_no_stats", 30, defence: 5);
|
||||
|
||||
var blocked = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToEnemy, "Formula=12"), hero, guard);
|
||||
ModAssert.Equal(7, blocked.Target.Damage, "the shield took 5");
|
||||
ModAssert.Equal(-5, blocked.Target.Defence, "shield change");
|
||||
ModAssert.Equal(0, guard.Attributes.Defence.Value, "the shield is spent");
|
||||
|
||||
guard.Attributes.Defence.Value = 5;
|
||||
var pierced = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToEnemy, "Formula=12", "Pierce=1"), hero, guard);
|
||||
ModAssert.Equal(12, pierced.Target.Damage, "pierce ignores the shield");
|
||||
ModAssert.Equal(5, guard.Attributes.Defence.Value, "the shield is kept");
|
||||
}
|
||||
|
||||
[ModTest("executor/heal_is_capped_and_targets_self")]
|
||||
static void HealIsCapped(ModTestContext ctx)
|
||||
{
|
||||
var hero = Fighter("test_hero_no_stats", 20);
|
||||
hero.Attributes.Health.Value = 15;
|
||||
|
||||
var result = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToSelf, "Formula=10", "Effect=heal"), hero, null);
|
||||
|
||||
ModAssert.True(result.Result, "applied");
|
||||
ModAssert.Equal(5, result.Target.Damage, "only the missing 5 are healed");
|
||||
ModAssert.Equal(20, hero.Attributes.Health.Value, "full");
|
||||
}
|
||||
|
||||
[ModTest("executor/points_refuse_and_negative_is_zero")]
|
||||
static void PointsRefuseAndNegativeIsZero(ModTestContext ctx)
|
||||
{
|
||||
var hero = Fighter("test_hero_no_stats", 20, points: 1);
|
||||
var slime = Fighter("test_slime_no_stats", 30);
|
||||
|
||||
var refused = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToEnemy, "Formula=50", "RequiredPoint=2"), hero, slime);
|
||||
ModAssert.False(refused.Result, "not enough action points");
|
||||
ModAssert.Equal(30, slime.Attributes.Health.Value, "nothing happened");
|
||||
ModAssert.Equal(1, hero.Attributes.Point.Value, "no points spent");
|
||||
|
||||
var zero = new FormulaActionExecutor("test/formula").Execute(Action(ActionCharacterType.ToEnemy, "Formula=b.hp - 999"), hero, slime);
|
||||
ModAssert.Equal(0, zero.Target.Damage, "a negative amount is 0");
|
||||
ModAssert.Equal(30, slime.Attributes.Health.Value, "never heals by accident");
|
||||
}
|
||||
|
||||
[ModTest("registry/formula_actions_are_detected")]
|
||||
static void FormulaActionsAreDetected(ModTestContext ctx)
|
||||
{
|
||||
ModAssert.True(FormulaActions.IsFormulaAction(new ActionCharacterInfo { ID = "x", Values = new() { "RequiredPoint=1", "formula=a.attack" } }), "Formula= in any case");
|
||||
ModAssert.False(FormulaActions.IsFormulaAction(new ActionCharacterInfo { ID = "y", Values = new() { "Attack=2" } }), "a core action");
|
||||
ModAssert.False(FormulaActions.IsFormulaAction(new ActionCharacterInfo { ID = "z" }), "no values");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user