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; /// /// Tests for the 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". /// public class LastResortTests { /// /// Creates a mocked with the given move name. /// private static ILearnedMove CreateLearnedMove(string name) { var moveData = Substitute.For(); moveData.Name.Returns(new StringKey(name)); moveData.SecondaryEffect.Returns((ISecondaryEffect?)null); var learnedMove = Substitute.For(); learnedMove.MoveData.Returns(moveData); return learnedMove; } /// /// Creates a mocked user in battle that knows the given moves. /// private static IPokemon CreateUser(params ILearnedMove?[] moves) { var user = Substitute.For(); user.Moves.Returns(moves); var battle = Substitute.For(); var battleData = Substitute.For(); battleData.Battle.Returns(battle); user.BattleData.Returns(battleData); return user; } /// /// Sets the battle's previous turn choices, one inner array per turn (oldest first). /// private static void SetPreviousTurns(IPokemon user, params ITurnChoice[][] turns) { user.BattleData!.Battle.PreviousTurnChoices.Returns(turns.Select(IReadOnlyList (t) => t).ToList()); } /// /// Runs for the user selecting the given Last Resort /// move and returns whether the selection was prevented. /// 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; } /// /// 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. /// [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(); } /// /// 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. /// [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(); } /// /// 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. /// [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(); } /// /// Bulbapedia: "Last Resort will fail if it is the only move the user knows". /// [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(); } /// /// 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. /// [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(); 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(); } /// /// 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. /// [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(); } /// /// 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. /// [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(); SetPreviousTurns(user, [new MoveChoice(other, tackle, 0, 0)]); // Act var prevent = RunPreventMoveSelection(user, lastResort); // Assert await Assert.That(prevent).IsTrue(); } /// /// Technical test: outside of battle (no ) the selection is prevented /// instead of throwing. /// [Test] public async Task PreventMoveSelection_NoBattleData_SelectionPrevented() { // Arrange var tackle = CreateLearnedMove("tackle"); var lastResort = CreateLearnedMove("last_resort"); var user = Substitute.For(); user.Moves.Returns([tackle, lastResort]); user.BattleData.Returns((IPokemonBattleData?)null); // Act var prevent = RunPreventMoveSelection(user, lastResort); // Assert await Assert.That(prevent).IsTrue(); } }