b451f83174
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
96 lines
2.9 KiB
C#
96 lines
2.9 KiB
C#
using Theriapolis.Core.Rules.Stats;
|
|
using Xunit;
|
|
|
|
namespace Theriapolis.Tests.Rules;
|
|
|
|
public sealed class AbilityScoreTests
|
|
{
|
|
[Theory]
|
|
[InlineData(1, -5)]
|
|
[InlineData(8, -1)]
|
|
[InlineData(9, -1)]
|
|
[InlineData(10, 0)]
|
|
[InlineData(11, 0)]
|
|
[InlineData(12, 1)]
|
|
[InlineData(15, 2)]
|
|
[InlineData(18, 4)]
|
|
[InlineData(20, 5)]
|
|
[InlineData(30, 10)]
|
|
public void Mod_MatchesD20Table(int score, int expected)
|
|
{
|
|
Assert.Equal(expected, AbilityScores.Mod(score));
|
|
}
|
|
|
|
[Fact]
|
|
public void Mod_FloorsTowardNegativeInfinity()
|
|
{
|
|
// Score 9 → -1, score 7 → -2 (per d20 floor convention; not C# truncate)
|
|
Assert.Equal(-1, AbilityScores.Mod(9));
|
|
Assert.Equal(-2, AbilityScores.Mod(7));
|
|
Assert.Equal(-3, AbilityScores.Mod(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ClampsToValidRange()
|
|
{
|
|
var a = new AbilityScores(0, 31, 50, -10, 100, 18);
|
|
Assert.Equal(1, a.STR); // 0 clamped up to 1
|
|
Assert.Equal(30, a.DEX); // 31 clamped down to 30
|
|
Assert.Equal(30, a.CON);
|
|
Assert.Equal(1, a.INT); // -10 clamped up to 1
|
|
Assert.Equal(30, a.WIS);
|
|
Assert.Equal(18, a.CHA);
|
|
}
|
|
|
|
[Fact]
|
|
public void StandardArray_IsCanonical()
|
|
{
|
|
Assert.Equal(new[] { 15, 14, 13, 12, 10, 8 }, AbilityScores.StandardArray);
|
|
}
|
|
|
|
[Fact]
|
|
public void Get_ReturnsValueByAbilityId()
|
|
{
|
|
var a = new AbilityScores(11, 13, 15, 10, 12, 14);
|
|
Assert.Equal(11, a.Get(AbilityId.STR));
|
|
Assert.Equal(13, a.Get(AbilityId.DEX));
|
|
Assert.Equal(15, a.Get(AbilityId.CON));
|
|
Assert.Equal(10, a.Get(AbilityId.INT));
|
|
Assert.Equal(12, a.Get(AbilityId.WIS));
|
|
Assert.Equal(14, a.Get(AbilityId.CHA));
|
|
}
|
|
|
|
[Fact]
|
|
public void With_ReturnsNewBlock_LeavesOriginalUnchanged()
|
|
{
|
|
var a = new AbilityScores(10, 10, 10, 10, 10, 10);
|
|
var b = a.With(AbilityId.STR, 18);
|
|
Assert.Equal(10, a.STR); // unchanged
|
|
Assert.Equal(18, b.STR);
|
|
Assert.Equal(10, b.DEX); // others copied
|
|
}
|
|
|
|
[Fact]
|
|
public void Plus_AppliesAllMods()
|
|
{
|
|
var a = new AbilityScores(10, 10, 10, 10, 10, 10);
|
|
var mods = new Dictionary<AbilityId, int> { { AbilityId.STR, 1 }, { AbilityId.WIS, 2 } };
|
|
var b = a.Plus(mods);
|
|
Assert.Equal(11, b.STR);
|
|
Assert.Equal(12, b.WIS);
|
|
Assert.Equal(10, b.DEX);
|
|
}
|
|
|
|
[Fact]
|
|
public void ModFor_UsesStandardFormula()
|
|
{
|
|
var a = new AbilityScores(15, 14, 13, 12, 10, 8);
|
|
Assert.Equal( 2, a.ModFor(AbilityId.STR)); // (15-10)/2 = 2
|
|
Assert.Equal( 2, a.ModFor(AbilityId.DEX)); // (14-10)/2 = 2
|
|
Assert.Equal( 1, a.ModFor(AbilityId.CON)); // (13-10)/2 = 1
|
|
Assert.Equal( 1, a.ModFor(AbilityId.INT));
|
|
Assert.Equal( 0, a.ModFor(AbilityId.WIS));
|
|
Assert.Equal(-1, a.ModFor(AbilityId.CHA));
|
|
}
|
|
}
|