255 lines
9.7 KiB
C#
255 lines
9.7 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.Models.Choices;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
|
using PkmnLib.Static;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="BatonPass"/> move script.
|
|
/// Behavior is verified against the Bulbapedia page for Baton Pass (Generation VII).
|
|
/// In this codebase, volatile scripts that are transferred by Baton Pass are marked with
|
|
/// <see cref="IBatonPassException"/> (e.g. <see cref="AutotomizeEffect"/>).
|
|
/// </summary>
|
|
public class BatonPassTests
|
|
{
|
|
/// <summary>
|
|
/// Creates a mocked Pokémon with a real volatile <see cref="ScriptSet"/> and a real
|
|
/// <see cref="StatBoostStatisticSet"/>.
|
|
/// </summary>
|
|
private static IPokemon CreateMockPokemon(out IScriptSet volatileSet)
|
|
{
|
|
var pokemon = Substitute.For<IPokemon>();
|
|
pokemon.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
|
var set = new ScriptSet(pokemon);
|
|
pokemon.Volatile.Returns(set);
|
|
pokemon.StatBoost.Returns(new StatBoostStatisticSet());
|
|
volatileSet = set;
|
|
return pokemon;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a fully mocked test setup for Baton Pass tests. The Pokémon to switch in is stored in the
|
|
/// move choice's <see cref="IMoveChoice.AdditionalData"/> under the <c>to_switch</c> key.
|
|
/// </summary>
|
|
private static (BatonPass script, IExecutingMove move, IPokemon user, IPokemon toSwitch, IBattleSide side,
|
|
IScriptSet userVolatile, IScriptSet switchInVolatile) CreateTestSetup()
|
|
{
|
|
var script = new BatonPass();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var user = CreateMockPokemon(out var userVolatile);
|
|
var toSwitch = CreateMockPokemon(out var switchInVolatile);
|
|
|
|
var moveChoice = Substitute.For<IMoveChoice>();
|
|
moveChoice.AdditionalData.Returns(new Dictionary<StringKey, object?> { { "to_switch", toSwitch } });
|
|
move.MoveChoice.Returns(moveChoice);
|
|
|
|
var side = Substitute.For<IBattleSide>();
|
|
var battle = Substitute.For<IBattle>();
|
|
battle.Sides.Returns(new[] { side });
|
|
|
|
var battleData = Substitute.For<IPokemonBattleData>();
|
|
battleData.Battle.Returns(battle);
|
|
battleData.SideIndex.Returns((byte)0);
|
|
battleData.Position.Returns((byte)1);
|
|
user.BattleData.Returns(battleData);
|
|
|
|
move.User.Returns(user);
|
|
return (script, move, user, toSwitch, side, userVolatile, switchInVolatile);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass switches out the user and passes several effects to the Pokémon switched in
|
|
/// its place."
|
|
/// The user is swapped out for the chosen Pokémon on its own field position.
|
|
/// </summary>
|
|
[Test]
|
|
public void OnSecondaryEffect_SwitchTargetChosen_SwitchesOutUser()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, toSwitch, side, _, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
side.Received(1).SwapPokemon(1, toSwitch);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass transfers all temporary stat stage increases and decreases".
|
|
/// Each stat stage (including evasion and accuracy) is copied to the Pokémon that is switched in.
|
|
/// </summary>
|
|
[Test, Arguments(Statistic.Attack, (sbyte)6), Arguments(Statistic.Defense, (sbyte)-6),
|
|
Arguments(Statistic.SpecialAttack, (sbyte)2), Arguments(Statistic.SpecialDefense, (sbyte)-1),
|
|
Arguments(Statistic.Speed, (sbyte)3), Arguments(Statistic.Evasion, (sbyte)1),
|
|
Arguments(Statistic.Accuracy, (sbyte)-2)]
|
|
public async Task OnSecondaryEffect_StatBoost_TransferredToSwitchIn(Statistic stat, sbyte boost)
|
|
{
|
|
// Arrange
|
|
var (script, move, user, toSwitch, _, _, _) = CreateTestSetup();
|
|
user.StatBoost.SetStatistic(stat, boost);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
await Assert.That(toSwitch.StatBoost.GetStatistic(stat)).IsEqualTo(boost);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass transfers all temporary stat stage increases and decreases".
|
|
/// After the stat stages are copied, the switched-in Pokémon's boosted stats are recalculated.
|
|
/// </summary>
|
|
[Test]
|
|
public void OnSecondaryEffect_StatBoostsTransferred_RecalculatesSwitchInStats()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, toSwitch, _, _, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
toSwitch.Received(1).RecalculateBoostedStats();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass switches out the user and passes several effects to the Pokémon switched in
|
|
/// its place."
|
|
/// A volatile script that is marked as transferable (<see cref="IBatonPassException"/>, such as
|
|
/// <see cref="AutotomizeEffect"/>) is passed to the switched-in Pokémon.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TransferableVolatile_PassedToSwitchIn()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, _, userVolatile, switchInVolatile) = CreateTestSetup();
|
|
userVolatile.Add(new AutotomizeEffect());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
await Assert.That(switchInVolatile.Get<AutotomizeEffect>()).IsNotNull();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass also passes some volatile status conditions, namely confusion, getting pumped,
|
|
/// escape prevention, seeding, Curse, Substitute, Ingrain, Aqua Ring" — effects outside that list are not
|
|
/// passed. The consecutive-protection counter (<see cref="ProtectionFailureScript"/>) is not transferable,
|
|
/// so it must not end up on the switched-in Pokémon.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_NonTransferableVolatile_NotPassedToSwitchIn()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, _, userVolatile, switchInVolatile) = CreateTestSetup();
|
|
userVolatile.Add(new ProtectionFailureScript());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
await Assert.That(switchInVolatile.Get<ProtectionFailureScript>()).IsNull();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass switches out the user and passes several effects to the Pokémon switched in
|
|
/// its place."
|
|
/// A transferable volatile must be passed even when the user also has a non-transferable volatile that
|
|
/// was added before it.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TransferableVolatileAfterNonTransferable_StillPassedToSwitchIn()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, _, userVolatile, switchInVolatile) = CreateTestSetup();
|
|
userVolatile.Add(new ProtectionFailureScript());
|
|
userVolatile.Add(new AutotomizeEffect());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
await Assert.That(switchInVolatile.Get<AutotomizeEffect>()).IsNotNull();
|
|
await Assert.That(switchInVolatile.Get<ProtectionFailureScript>()).IsNull();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Baton Pass switches out the user".
|
|
/// The user's volatile scripts leave the field with it: after the switch its volatile containers are empty.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_UserSwitchedOut_UserVolatilesCleared()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, _, userVolatile, _) = CreateTestSetup();
|
|
userVolatile.Add(new AutotomizeEffect());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
await Assert.That(userVolatile.Get<AutotomizeEffect>()).IsNull();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: without any <see cref="IMoveChoice.AdditionalData"/> there is no Pokémon to switch to,
|
|
/// so no switch happens.
|
|
/// </summary>
|
|
[Test]
|
|
public void OnSecondaryEffect_NoAdditionalData_DoesNotSwitch()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, side, _, _) = CreateTestSetup();
|
|
move.MoveChoice.AdditionalData.Returns((Dictionary<StringKey, object?>?)null);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: if the stored switch target is not a Pokémon, no switch happens.
|
|
/// </summary>
|
|
[Test]
|
|
public void OnSecondaryEffect_SwitchTargetNotAPokemon_DoesNotSwitch()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, side, _, _) = CreateTestSetup();
|
|
move.MoveChoice.AdditionalData.Returns(new Dictionary<StringKey, object?> { { "to_switch", null } });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: if the user has no <see cref="IPokemon.BattleData"/>, the script returns without
|
|
/// switching and without clearing the user's volatile scripts.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_NoBattleData_DoesNotSwitch()
|
|
{
|
|
// Arrange
|
|
var (script, move, user, _, side, userVolatile, _) = CreateTestSetup();
|
|
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
userVolatile.Add(new AutotomizeEffect());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, user, 0);
|
|
|
|
// Assert
|
|
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
|
await Assert.That(userVolatile.Get<AutotomizeEffect>()).IsNotNull();
|
|
}
|
|
} |