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

@@ -6,65 +6,78 @@ using PkmnLib.Tests.Integration;
namespace PkmnLib.Tests.Dynamic;
/// <summary>
/// Tests for the temporary move overlay (<see cref="IPokemon.LearnTemporaryMove"/>), used by effects such as
/// Mimic. The permanently learned moves must never be mutated by a temporary move, and the overlay must be
/// discarded by the engine itself when the Pokemon leaves the battlefield.
/// Tests for the temporary move overlay (<see cref="IBattlePokemon.LearnTemporaryMove"/>), used by effects
/// such as Mimic. The permanently learned moves must never be mutated by a temporary move, and the overlay
/// must be discarded by the engine itself when the Pokemon leaves the battlefield.
/// </summary>
public class PokemonTemporaryMoveTests
{
private static IPokemon CreatePokemon()
private static (IBattle battle, IBattlePokemon battlePokemon, IPokemon pokemon) CreateBattlePokemon()
{
var library = LibraryHelpers.LoadLibrary();
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
throw new InvalidOperationException("Failed to load bulbasaur species.");
var pokemon = new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
{
Index = 0,
IsHidden = false,
}, 50, 0, Gender.Male, 0, "hardy");
IPokemon CreateBulbasaur() =>
new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
{
Index = 0,
IsHidden = false,
}, 50, 0, Gender.Male, 0, "hardy");
var pokemon = CreateBulbasaur();
pokemon.LearnMove("tackle", MoveLearnMethod.LevelUp, 0);
return pokemon;
var party = new PokemonPartyImpl(1);
party.SwapInto(pokemon, 0);
var opponentParty = new PokemonPartyImpl(1);
opponentParty.SwapInto(CreateBulbasaur(), 0);
var parties = new[]
{
new BattlePartyImpl(party, [new ResponsibleIndex(0, 0)]),
new BattlePartyImpl(opponentParty, [new ResponsibleIndex(1, 0)]),
};
var battle = new BattleImpl(library, parties, false, 2, 1, false, "grass", 0);
return (battle, battle.Parties[0].GetBattlePokemon(pokemon)!, pokemon);
}
[Test]
public async Task LearnTemporaryMove_ReplacesMoveInMovesButNotInBaseMoves()
{
var pokemon = CreatePokemon();
var (_, battlePokemon, pokemon) = CreateBattlePokemon();
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
await Assert.That(pokemon.Moves[0]!.LearnMethod).IsEqualTo(MoveLearnMethod.Mimic);
await Assert.That(pokemon.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
await Assert.That(battlePokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
await Assert.That(battlePokemon.Moves[0]!.LearnMethod).IsEqualTo(MoveLearnMethod.Mimic);
await Assert.That(battlePokemon.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
}
[Test]
public async Task SetOnBattlefield_LeavingField_RestoresOriginalMoveWithItsPP()
public async Task OnSwitchedOut_RestoresOriginalMoveWithItsPP()
{
var pokemon = CreatePokemon();
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
pokemon.SetOnBattlefield(true);
var (_, battlePokemon, _) = CreateBattlePokemon();
battlePokemon.OnSwitchedIn(0);
// Use the original move once, so we can verify its PP survives the temporary replacement untouched.
var originalMove = pokemon.Moves[0]!;
var originalMove = battlePokemon.Moves[0]!;
originalMove.TryUse();
var expectedPp = originalMove.CurrentPp;
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
pokemon.SetOnBattlefield(false);
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
battlePokemon.OnSwitchedOut();
await Assert.That(ReferenceEquals(pokemon.Moves[0], originalMove)).IsTrue();
await Assert.That(pokemon.Moves[0]!.CurrentPp).IsEqualTo(expectedPp);
await Assert.That(ReferenceEquals(battlePokemon.Moves[0], originalMove)).IsTrue();
await Assert.That(battlePokemon.Moves[0]!.CurrentPp).IsEqualTo(expectedPp);
}
[Test]
public async Task ClearBattleData_RestoresOriginalMove()
public async Task BattleEnd_LeavesOriginalMovesUntouched()
{
var pokemon = CreatePokemon();
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
var (battle, battlePokemon, pokemon) = CreateBattlePokemon();
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
pokemon.ClearBattleData();
battle.Dispose();
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
}
@@ -72,10 +85,10 @@ public class PokemonTemporaryMoveTests
[Test]
public async Task Serialize_WithActiveTemporaryMove_WritesOriginalMove()
{
var pokemon = CreatePokemon();
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
var (_, battlePokemon, _) = CreateBattlePokemon();
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
var serialized = pokemon.Serialize();
var serialized = battlePokemon.Serialize();
await Assert.That(serialized.Moves[0]!.MoveName.ToString()).IsEqualTo("tackle");
}