Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/LastResortTests.cs
Deukhoofd d2a82b5fe3
All checks were successful
Build / Build (push) Successful in 3m12s
Many more tests and fixes
2026-08-27 17:11:12 +02:00

233 lines
8.6 KiB
C#

using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="LastResort"/> move script.
/// Gen VII Bulbapedia behavior: "Last Resort will fail unless the Pokémon has used all of its other moves
/// at least once while on the field. Using a move before switching out and back in does not count towards
/// being able to use Last Resort. Last Resort will fail if it is the only move the user knows".
/// </summary>
public class LastResortTests
{
/// <summary>
/// Creates a mocked <see cref="ILearnedMove"/> with the given move name.
/// </summary>
private static ILearnedMove CreateLearnedMove(string name)
{
var moveData = Substitute.For<IMoveData>();
moveData.Name.Returns(new StringKey(name));
moveData.SecondaryEffect.Returns((ISecondaryEffect?)null);
var learnedMove = Substitute.For<ILearnedMove>();
learnedMove.MoveData.Returns(moveData);
return learnedMove;
}
/// <summary>
/// Creates a mocked user in battle that knows the given moves.
/// </summary>
private static IPokemon CreateUser(params ILearnedMove?[] moves)
{
var user = Substitute.For<IPokemon>();
user.Moves.Returns(moves);
var battle = Substitute.For<IBattle>();
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
user.BattleData.Returns(battleData);
return user;
}
/// <summary>
/// Sets the battle's previous turn choices, one inner array per turn (oldest first).
/// </summary>
private static void SetPreviousTurns(IPokemon user, params ITurnChoice[][] turns)
{
user.BattleData!.Battle.PreviousTurnChoices.Returns(turns.Select(IReadOnlyList<ITurnChoice> (t) => t).ToList());
}
/// <summary>
/// Runs <see cref="LastResort.PreventMoveSelection"/> for the user selecting the given Last Resort
/// move and returns whether the selection was prevented.
/// </summary>
private static bool RunPreventMoveSelection(IPokemon user, ILearnedMove lastResort)
{
var script = new LastResort();
var choice = new MoveChoice(user, lastResort, 0, 0);
var prevent = false;
script.PreventMoveSelection(choice, ref prevent);
return prevent;
}
/// <summary>
/// Bulbapedia: "Last Resort will fail unless the Pokémon has used all of its other moves at least once
/// while on the field." — with every other move used, selecting Last Resort is allowed.
/// </summary>
[Test]
public async Task PreventMoveSelection_AllOtherMovesUsedOnField_SelectionAllowed()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var growl = CreateLearnedMove("growl");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, growl, lastResort);
SetPreviousTurns(user, [new MoveChoice(user, tackle, 0, 0)], [new MoveChoice(user, growl, 0, 0)]);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsFalse();
}
/// <summary>
/// Bulbapedia: "Last Resort will fail unless the Pokémon has used all of its other moves at least once
/// while on the field." — with one of the other moves never used, the selection is prevented.
/// </summary>
[Test]
public async Task PreventMoveSelection_NotAllOtherMovesUsed_SelectionPrevented()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var growl = CreateLearnedMove("growl");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, growl, lastResort);
SetPreviousTurns(user, [new MoveChoice(user, tackle, 0, 0)]);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
/// <summary>
/// Bulbapedia: Last Resort fails "unless the Pokémon has used all of its other moves at least once" —
/// with no moves used at all (e.g. the first turn), the selection is prevented.
/// </summary>
[Test]
public async Task PreventMoveSelection_NoMovesUsedYet_SelectionPrevented()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, lastResort);
SetPreviousTurns(user);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
/// <summary>
/// Bulbapedia: "Last Resort will fail if it is the only move the user knows".
/// </summary>
[Test]
public async Task PreventMoveSelection_LastResortOnlyKnownMove_SelectionPrevented()
{
// Arrange
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(lastResort, null, null, null);
SetPreviousTurns(user);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
/// <summary>
/// Bulbapedia: "Using a move before switching out and back in does not count towards being able to use
/// Last Resort." — a move used only before the user was switched back in does not enable Last Resort.
/// </summary>
[Test]
public async Task PreventMoveSelection_MoveOnlyUsedBeforeSwitchingOut_SelectionPrevented()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, lastResort);
var ally = Substitute.For<IPokemon>();
SetPreviousTurns(user, [new MoveChoice(user, tackle, 0, 0)], [new SwitchChoice(user, ally)],
[new SwitchChoice(ally, user)]);
// The user re-entered the field during turn 3, after all three recorded turns' choices were made.
user.BattleData!.SwitchInTurn.Returns(3u);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
/// <summary>
/// Bulbapedia: "A move will still count as used for Last Resort even if it would have no effect, so for
/// example, using Snore when the user is awake or a move missing due to accuracy or evasion." — a failed
/// move choice still counts as used.
/// </summary>
[Test]
public async Task PreventMoveSelection_OtherMoveFailed_SelectionAllowed()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, lastResort);
var failedTackle = new MoveChoice(user, tackle, 0, 0);
failedTackle.Fail();
SetPreviousTurns(user, [failedTackle]);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsFalse();
}
/// <summary>
/// Bulbapedia: the requirement is that "the Pokémon has used all of its other moves" — a move used by a
/// different Pokémon does not count for the user.
/// </summary>
[Test]
public async Task PreventMoveSelection_MoveUsedByAnotherPokemon_SelectionPrevented()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var lastResort = CreateLearnedMove("last_resort");
var user = CreateUser(tackle, lastResort);
var other = Substitute.For<IPokemon>();
SetPreviousTurns(user, [new MoveChoice(other, tackle, 0, 0)]);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
/// <summary>
/// Technical test: outside of battle (no <see cref="IPokemon.BattleData"/>) the selection is prevented
/// instead of throwing.
/// </summary>
[Test]
public async Task PreventMoveSelection_NoBattleData_SelectionPrevented()
{
// Arrange
var tackle = CreateLearnedMove("tackle");
var lastResort = CreateLearnedMove("last_resort");
var user = Substitute.For<IPokemon>();
user.Moves.Returns([tackle, lastResort]);
user.BattleData.Returns((IPokemonBattleData?)null);
// Act
var prevent = RunPreventMoveSelection(user, lastResort);
// Assert
await Assert.That(prevent).IsTrue();
}
}