Move all battle state from IPokemon to an ephemeral IBattlePokemon wrapper

This commit is contained in:
2026-08-28 15:20:26 +02:00
parent 942be8eaeb
commit 8a2733a0a9
859 changed files with 4408 additions and 5331 deletions

View File

@@ -22,12 +22,12 @@ public class ForestsCurseTests
/// Creates a fully mocked test setup for Forest's Curse tests. The target is in battle, with a mocked
/// battle library chain that can resolve the Grass type identifier.
/// </summary>
private static (ForestsCurse script, IExecutingMove move, IPokemon target, IHitData hitData) CreateTestSetup(
private static (ForestsCurse script, IExecutingMove move, IBattlePokemon target, IHitData hitData) CreateTestSetup(
bool grassTypeInLibrary = true, TypeIdentifier[]? targetTypes = null)
{
var script = new ForestsCurse();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var target = Substitute.For<IBattlePokemon>();
var hitData = Substitute.For<IHitData>();
move.GetHitData(target, 0).Returns(hitData);
@@ -47,18 +47,16 @@ public class ForestsCurseTests
library.StaticLibrary.Returns(staticLibrary);
var battle = Substitute.For<IBattle>();
battle.Library.Returns(library);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
target.BattleData.Returns(battleData);
target.Battle.Returns(battle);
target.Types.Returns(targetTypes ?? [NormalType]);
return (script, move, target, hitData);
}
/// <summary>
/// Helper to extract the type passed to the target's <see cref="IPokemon.AddType"/> call.
/// Helper to extract the type passed to the target's <see cref="IBattlePokemon.AddType"/> call.
/// </summary>
private static TypeIdentifier? GetAddedType(IPokemon target)
private static TypeIdentifier? GetAddedType(IBattlePokemon target)
{
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "AddType");
return call != null ? (TypeIdentifier)call.GetArguments()[0]! : null;
@@ -85,7 +83,7 @@ public class ForestsCurseTests
/// <summary>
/// Bulbapedia: the Grass type is added "in addition to the Pokémon's original type(s)".
/// The target's original types may not be replaced, so <see cref="IPokemon.SetTypes"/> must not be used.
/// The target's original types may not be replaced, so <see cref="IBattlePokemon.SetTypes"/> must not be used.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetInBattle_OriginalTypesNotReplaced()
@@ -117,24 +115,6 @@ public class ForestsCurseTests
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
}
/// <summary>
/// If the target has no <see cref="IPokemon.BattleData"/>, the script cannot resolve the type library,
/// so no type may be added.
/// </summary>
[Test]
public async Task OnSecondaryEffect_NullBattleData_DoesNotAddType()
{
// Arrange
var (script, move, target, _) = CreateTestSetup();
target.BattleData.Returns((IPokemonBattleData?)null);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "AddType")).IsFalse();
}
/// <summary>
/// Technical test: when the type library cannot resolve the Grass type identifier, the script must
/// bail out without adding a type.
@@ -158,8 +138,8 @@ public class ForestsCurseTests
/// A target whose Ghost type was added by <see cref="TrickOrTreat"/> must end up with the Grass type and
/// without the Ghost type, while keeping its original type. The mocked target uses a real
/// <see cref="ScriptSet"/> for its volatile scripts, so the <c>HasHadTypeAddedEffect</c> marker set by
/// Trick-or-Treat is visible to Forest's Curse, and reacts to <see cref="IPokemon.AddType"/> and
/// <see cref="IPokemon.RemoveType"/> with a backing type list, mirroring the real Pokemon implementation,
/// Trick-or-Treat is visible to Forest's Curse, and reacts to <see cref="IBattlePokemon.AddType"/> and
/// <see cref="IBattlePokemon.RemoveType"/> with a backing type list, mirroring the real Pokemon implementation,
/// so the resulting type list is observable.
/// </summary>
[Test]
@@ -169,7 +149,7 @@ public class ForestsCurseTests
var forestsCurse = new ForestsCurse();
var trickOrTreat = new TrickOrTreat();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var target = Substitute.For<IBattlePokemon>();
var typeLibrary = Substitute.For<IReadOnlyTypeLibrary>();
typeLibrary.TryGetTypeIdentifier(new StringKey("grass"), out Arg.Any<TypeIdentifier>()).Returns(x =>
@@ -189,9 +169,7 @@ public class ForestsCurseTests
var battle = Substitute.For<IBattle>();
battle.Library.Returns(library);
move.Battle.Returns(battle);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
target.BattleData.Returns(battleData);
target.Battle.Returns(battle);
// Real script set so the HasHadTypeAddedEffect marker added by Trick-or-Treat is visible
// to Forest's Curse.