using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile; using PkmnLib.Static.Moves; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script. /// Bulbapedia: "When allies use Grass Pledge paired with Fire or Water Pledge simultaneously, the /// first-moving ally skips their turn while the second-moving ally executes a combined attack with 150 /// power" and a unique field effect. /// public class GrassPledgeTests { /// /// Creates a move choice for a Pokémon on the given side, using a move with the given name. /// The choice gets a real as its volatile set, so combination markers can be /// added to and read from it. /// private static IMoveChoice CreateQueuedChoice(string moveName, byte sideIndex) { var choice = Substitute.For(); choice.GetScripts().Returns(_ => new ScriptIterator(new List>())); var pokemon = Substitute.For(); pokemon.SideIndex.Returns(sideIndex); choice.User.Returns(pokemon); var moveData = Substitute.For(); moveData.Name.Returns(new StringKey(moveName)); var learnedMove = Substitute.For(); learnedMove.MoveData.Returns(moveData); choice.ChosenMove.Returns(learnedMove); var choiceVolatile = new ScriptSet(choice); choice.Volatile.Returns(choiceVolatile); return choice; } /// /// Creates a fully mocked test setup for Grass Pledge tests. The user is on side 0. /// private static (GrassPledge script, IExecutingMove move, IMoveChoice ownChoice) CreateTestSetup( BattleChoiceQueue? queue) { var script = new GrassPledge(); var move = Substitute.For(); var battle = Substitute.For(); battle.ChoiceQueue.Returns(queue); move.Battle.Returns(battle); var user = Substitute.For(); user.SideIndex.Returns((byte)0); move.User.Returns(user); var ownChoice = CreateQueuedChoice("grass_pledge", 0); move.MoveChoice.Returns(ownChoice); return (script, move, ownChoice); } /// /// Bulbapedia: "the first-moving ally skips their turn". An ally on the same side still has Water /// Pledge queued, so this Grass Pledge is stopped. /// [Test] public async Task StopBeforeMove_AllyWaterPledgeQueued_StopsMove() { // Arrange var allyChoice = CreateQueuedChoice("water_pledge", 0); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsTrue(); } /// /// Bulbapedia: Grass Pledge combined with Water Pledge "produces a swamp on the target's side of the /// field for four turns". The queued ally choice is marked with the /// volatile that implements the combined move. /// [Test] public async Task StopBeforeMove_AllyWaterPledgeQueued_MarksAllyChoiceAsCombinedGrassWaterPledge() { // Arrange var allyChoice = CreateQueuedChoice("water_pledge", 0); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(allyChoice.Volatile.Contains()).IsTrue(); } /// /// Bulbapedia: "the first-moving ally skips their turn". An ally on the same side still has Fire /// Pledge queued, so this Grass Pledge is stopped. /// [Test] public async Task StopBeforeMove_AllyFirePledgeQueued_StopsMove() { // Arrange var allyChoice = CreateQueuedChoice("fire_pledge", 0); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsTrue(); } /// /// Bulbapedia: Grass Pledge combined with Fire Pledge "creates a sea of fire on the target's side of /// the field for four turns". The queued ally choice is marked with the /// volatile that implements the combined move. /// [Test] public async Task StopBeforeMove_AllyFirePledgeQueued_MarksAllyChoiceAsCombinedFireGrassPledge() { // Arrange var allyChoice = CreateQueuedChoice("fire_pledge", 0); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(allyChoice.Volatile.Contains()).IsTrue(); } /// /// Bulbapedia: "When used alone, it deals damage" — no Pledge move is queued by an ally, so Grass /// Pledge executes normally. /// [Test] public async Task StopBeforeMove_NoPledgeMoveQueued_DoesNotStop() { // Arrange var allyChoice = CreateQueuedChoice("tackle", 0); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsFalse(); } /// /// Bulbapedia: the combination only happens "when allies use" the paired Pledge moves — a Pledge move /// queued by an opponent does not combine. /// [Test] public async Task StopBeforeMove_OpponentWaterPledgeQueued_DoesNotStop() { // Arrange var opponentChoice = CreateQueuedChoice("water_pledge", 1); var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([opponentChoice])); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsFalse(); await Assert.That(opponentChoice.Volatile.Contains()).IsFalse(); } /// /// Bulbapedia: "the second-moving ally executes a combined attack with 150 power". When this Grass /// Pledge choice is itself the combined move of Fire Pledge and Grass Pledge (marked with /// ), it must execute instead of deferring again. /// [Test] public async Task StopBeforeMove_ChoiceIsCombinedFireGrassPledge_DoesNotStop() { // Arrange var allyChoice = CreateQueuedChoice("fire_pledge", 0); var (script, move, ownChoice) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); ownChoice.Volatile.Add(new FireGrassPledgeMove()); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsFalse(); await Assert.That(allyChoice.Volatile.Contains()).IsFalse(); } /// /// Bulbapedia: "the second-moving ally executes a combined attack with 150 power". When this choice is /// the combined move of Grass Pledge and Water Pledge (marked with ), /// it must execute instead of deferring again. /// [Test] public async Task StopBeforeMove_ChoiceIsCombinedGrassWaterPledge_DoesNotStop() { // Arrange var allyChoice = CreateQueuedChoice("water_pledge", 0); var (script, move, ownChoice) = CreateTestSetup(new BattleChoiceQueue([allyChoice])); ownChoice.Volatile.Add(new GrassWaterPledgeMove()); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsFalse(); await Assert.That(allyChoice.Volatile.Contains()).IsFalse(); } /// /// Technical test: when the battle has no active choice queue, the move executes normally without /// throwing. /// [Test] public async Task StopBeforeMove_NullChoiceQueue_DoesNotStop() { // Arrange var (script, move, _) = CreateTestSetup(null); var stop = false; // Act script.StopBeforeMove(move, ref stop); // Assert await Assert.That(stop).IsFalse(); } }