142 lines
5.7 KiB
C#
142 lines
5.7 KiB
C#
using PkmnLib.Dynamic.Events;
|
|
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
|
using PkmnLib.Static;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Flatter"/> move script.
|
|
/// Gen VII Bulbapedia behavior: Flatter raises the target's Special Attack stat by one stage and confuses it.
|
|
/// </summary>
|
|
public class FlatterTests
|
|
{
|
|
private static (Flatter script, IExecutingMove move, IPokemon target, IScriptSet targetVolatile) CreateTestSetup()
|
|
{
|
|
var script = new Flatter();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var user = Substitute.For<IPokemon>();
|
|
move.User.Returns(user);
|
|
|
|
var target = Substitute.For<IPokemon>();
|
|
var targetVolatile = Substitute.For<IScriptSet>();
|
|
target.Volatile.Returns(targetVolatile);
|
|
|
|
return (script, move, target, targetVolatile);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the arguments of the received ChangeStatBoost call, or null when no stat boost
|
|
/// was requested. Received-call inspection is used instead of NSubstitute argument matchers because
|
|
/// the trailing <see cref="EventBatchId"/> parameter cannot be bound by <c>Arg.Any</c> (its
|
|
/// parameterless constructor initializes a fresh id, so it never equals the matcher's default value).
|
|
/// </summary>
|
|
private static object?[]? GetStatBoostArgs(IPokemon pokemon)
|
|
{
|
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
|
return call?.GetArguments();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Flatter "raises the target's Special Attack stat by one stage".
|
|
/// The boost is caused by the opponent's move, so it is not self-inflicted.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Always_RaisesTargetSpecialAttackByOneStage()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var args = GetStatBoostArgs(target);
|
|
await Assert.That(args).IsNotNull();
|
|
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.SpecialAttack);
|
|
await Assert.That((sbyte)args[1]!).IsEqualTo((sbyte)1);
|
|
await Assert.That((bool)args[2]!).IsFalse(); // not self-inflicted
|
|
await Assert.That((bool)args[3]!).IsFalse(); // not forced
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Flatter raises the target's Special Attack "and confuses it". The confusion is applied
|
|
/// through the target's <see cref="IPokemon.Volatile"/> script set.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Always_ConfusesTarget()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, targetVolatile) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
targetVolatile.Received(1).StackOrAdd(new StringKey("confusion"), Arg.Any<Func<Script?>>());
|
|
await Assert.That(targetVolatile.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "StackOrAdd")).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Flatter "confuses" the target. The instantiation function passed to the volatile script
|
|
/// set creates a <see cref="Confusion"/> script.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_ConfusionInstantiation_CreatesConfusionScript()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, targetVolatile) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var call = targetVolatile.ReceivedCalls().First(c => c.GetMethodInfo().Name == "StackOrAdd");
|
|
var instantiation = (Func<Script?>)call.GetArguments()[1]!;
|
|
await Assert.That(instantiation() is Confusion).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Even if the target's Special Attack is already at +6 stages, it will still become
|
|
/// confused." A substitute's <see cref="IPokemon.ChangeStatBoost"/> returns false by default (the
|
|
/// boost failed), yet the confusion is still applied.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_StatBoostFails_TargetStillConfused()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, targetVolatile) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert - the unconfigured ChangeStatBoost returned false, but the target was still confused
|
|
await Assert.That(GetStatBoostArgs(target)).IsNotNull();
|
|
await Assert.That(targetVolatile.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "StackOrAdd")).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Even if the target is already confused, or cannot become confused due to Safeguard or
|
|
/// Own Tempo, its Special Attack will still be raised." When the volatile set refuses to add the
|
|
/// confusion script (returns null), the Special Attack boost still happens.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_ConfusionNotAdded_SpecialAttackStillRaised()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, targetVolatile) = CreateTestSetup();
|
|
targetVolatile.StackOrAdd(Arg.Any<StringKey>(), Arg.Any<Func<Script?>>()).Returns((ScriptContainer?)null);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var args = GetStatBoostArgs(target);
|
|
await Assert.That(args).IsNotNull();
|
|
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.SpecialAttack);
|
|
await Assert.That((sbyte)args[1]!).IsEqualTo((sbyte)1);
|
|
}
|
|
} |