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();
var battleData = Substitute.For();
battleData.SideIndex.Returns(sideIndex);
pokemon.BattleData.Returns(battleData);
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();
var userBattleData = Substitute.For();
userBattleData.SideIndex.Returns((byte)0);
user.BattleData.Returns(userBattleData);
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();
}
}