251 lines
9.7 KiB
C#
251 lines
9.7 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Dynamic.ScriptHandling.Registry;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
|
using PkmnLib.Static.Utils;
|
|
using BattleGravity = PkmnLib.Plugin.Gen7.Scripts.Battle.Gravity;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Gravity"/> move script.
|
|
/// Gen VII Bulbapedia behavior: "Gravity causes the field to undergo intense gravity lasting 5 turns",
|
|
/// and "Semi-invulnerable Pokémon using Fly or Bounce are immediately canceled."
|
|
/// </summary>
|
|
public class GravityTests
|
|
{
|
|
/// <summary>
|
|
/// Creates a fully mocked test setup for Gravity tests. The battle's volatile script set is a
|
|
/// substitute so the addition of the battle-wide gravity script can be inspected.
|
|
/// </summary>
|
|
private static (Gravity script, IExecutingMove move, IPokemon target, IBattle battle, IScriptSet battleVolatile)
|
|
CreateTestSetup(params IReadOnlyList<IPokemon?>[] sidePokemon)
|
|
{
|
|
var script = new Gravity();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
move.User.Returns(Substitute.For<IPokemon>());
|
|
|
|
var battle = Substitute.For<IBattle>();
|
|
var battleVolatile = Substitute.For<IScriptSet>();
|
|
battle.Volatile.Returns(battleVolatile);
|
|
var sides = sidePokemon.Select(pokemon =>
|
|
{
|
|
var side = Substitute.For<IBattleSide>();
|
|
side.Pokemon.Returns(pokemon);
|
|
return side;
|
|
}).ToArray();
|
|
battle.Sides.Returns(sides);
|
|
|
|
var battleData = Substitute.For<IPokemonBattleData>();
|
|
battleData.Battle.Returns(battle);
|
|
var target = Substitute.For<IPokemon>();
|
|
target.BattleData.Returns(battleData);
|
|
|
|
return (script, move, target, battle, battleVolatile);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a mocked Pokémon with a real <see cref="ScriptSet"/> as its volatile set, so effects can
|
|
/// be attached and their removal inspected.
|
|
/// </summary>
|
|
private static (IPokemon pokemon, ScriptSet volatileSet) CreateFieldPokemon()
|
|
{
|
|
var pokemon = Substitute.For<IPokemon>();
|
|
pokemon.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
|
var volatileSet = new ScriptSet(pokemon);
|
|
pokemon.Volatile.Returns(volatileSet);
|
|
return (pokemon, volatileSet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Gravity causes the field to undergo intense gravity lasting 5 turns." The battle-wide
|
|
/// gravity effect is added to the battle's volatile script set.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Always_AddsGravityToBattleVolatile()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _, battleVolatile) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
battleVolatile.Received(1).StackOrAdd(new StringKey("gravity"), Arg.Any<Func<Script?>>());
|
|
await Assert.That(battleVolatile.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "StackOrAdd")).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: the field undergoes "intense gravity" — the instantiation function passed to the
|
|
/// battle's volatile script set creates the battle-wide <see cref="BattleGravity"/> script.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_GravityInstantiation_CreatesBattleGravityScript()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, battle, battleVolatile) = CreateTestSetup();
|
|
battle.Library.Returns(LibraryHelpers.LoadLibrary());
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var call = battleVolatile.ReceivedCalls().First(c => c.GetMethodInfo().Name == "StackOrAdd");
|
|
var instantiation = (Func<Script?>)call.GetArguments()[1]!;
|
|
await Assert.That(instantiation() is BattleGravity).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Semi-invulnerable Pokémon using Fly or Bounce are immediately canceled." A Pokémon in
|
|
/// the semi-invulnerable turn of Fly has its <see cref="ChargeFlyEffect"/> removed.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_PokemonFlying_FlyEffectRemoved()
|
|
{
|
|
// Arrange
|
|
var (pokemon, volatileSet) = CreateFieldPokemon();
|
|
volatileSet.Add(new ChargeFlyEffect(pokemon));
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { pokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<ChargeFlyEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Semi-invulnerable Pokémon using Fly or Bounce are immediately canceled." A Pokémon in
|
|
/// the semi-invulnerable turn of Bounce has its <see cref="ChargeBounceEffect"/> removed.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_PokemonBouncing_BounceEffectRemoved()
|
|
{
|
|
// Arrange
|
|
var (pokemon, volatileSet) = CreateFieldPokemon();
|
|
volatileSet.Add(new ChargeBounceEffect(pokemon));
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { pokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<ChargeBounceEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Gravity cancels Sky Drop — "Semi-invulnerable Pokémon ... are immediately canceled." A
|
|
/// Pokémon in the semi-invulnerable turn of Sky Drop has its <see cref="ChargeSkyDropEffect"/>
|
|
/// removed.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_PokemonInSkyDrop_SkyDropEffectRemoved()
|
|
{
|
|
// Arrange
|
|
var (pokemon, volatileSet) = CreateFieldPokemon();
|
|
volatileSet.Add(new ChargeSkyDropEffect(pokemon));
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { pokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<ChargeSkyDropEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "The effect grounds all Pokémon, removing ... Telekinesis ... effects." A Pokémon under
|
|
/// Telekinesis has its <see cref="TelekinesisEffect"/> removed.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_PokemonUnderTelekinesis_TelekinesisEffectRemoved()
|
|
{
|
|
// Arrange
|
|
var (pokemon, volatileSet) = CreateFieldPokemon();
|
|
volatileSet.Add(new TelekinesisEffect());
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { pokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<TelekinesisEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "The effect grounds all Pokémon, removing ... Magnet Rise ... effects." A Pokémon under
|
|
/// Magnet Rise should have its <see cref="MagnetRiseEffect"/> removed.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_PokemonUnderMagnetRise_MagnetRiseEffectRemoved()
|
|
{
|
|
// Arrange
|
|
var (pokemon, volatileSet) = CreateFieldPokemon();
|
|
volatileSet.Add(new MagnetRiseEffect());
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { pokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<MagnetRiseEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "The effect grounds all Pokémon" — Pokémon on every side of the battle are affected,
|
|
/// not only those on the target's side.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_FlyingPokemonOnBothSides_BothCanceled()
|
|
{
|
|
// Arrange
|
|
var (allyPokemon, allyVolatile) = CreateFieldPokemon();
|
|
allyVolatile.Add(new ChargeFlyEffect(allyPokemon));
|
|
var (opposingPokemon, opposingVolatile) = CreateFieldPokemon();
|
|
opposingVolatile.Add(new ChargeFlyEffect(opposingPokemon));
|
|
var (script, move, target, _, _) = CreateTestSetup(new List<IPokemon?> { allyPokemon },
|
|
new List<IPokemon?> { opposingPokemon });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(allyVolatile.Contains(ScriptUtils.ResolveName<ChargeFlyEffect>())).IsFalse();
|
|
await Assert.That(opposingVolatile.Contains(ScriptUtils.ResolveName<ChargeFlyEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: empty (null) slots on a side are skipped without throwing.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_EmptyPokemonSlot_IsSkipped()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _, battleVolatile) = CreateTestSetup(new List<IPokemon?> { null });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(battleVolatile.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "StackOrAdd")).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: when the target has no <see cref="IPokemon.BattleData"/> (it is not in a battle),
|
|
/// the script does nothing instead of throwing.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_NullBattleData_DoesNothing()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _, battleVolatile) = CreateTestSetup();
|
|
target.BattleData.Returns((IPokemonBattleData?)null);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(battleVolatile.ReceivedCalls().Any()).IsFalse();
|
|
}
|
|
} |