156 lines
6.2 KiB
C#
156 lines
6.2 KiB
C#
using PkmnLib.Dynamic.Events;
|
|
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Static;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Memento"/> move script.
|
|
/// Gen VII Bulbapedia behavior: "Memento causes the user to faint and in return lowers the Attack and
|
|
/// Special Attack stat of the target by two stages each." From Generation V onward the user does not faint
|
|
/// if the move fails (no target, miss, blocked by a substitute) - that part is engine behavior, since
|
|
/// <see cref="Memento.OnSecondaryEffect"/> only runs when the move successfully hits.
|
|
/// </summary>
|
|
public class MementoTests
|
|
{
|
|
private static (Memento script, IExecutingMove move, IBattlePokemon target, IBattlePokemon user) CreateTestSetup()
|
|
{
|
|
var script = new Memento();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var target = Substitute.For<IBattlePokemon>();
|
|
var user = Substitute.For<IBattlePokemon>();
|
|
move.User.Returns(user);
|
|
return (script, move, target, user);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the arguments of the received ChangeStatBoost call for the given stat, or null
|
|
/// when no boost for that stat 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>.
|
|
/// </summary>
|
|
private static object?[]? GetStatBoostArgs(IBattlePokemon pokemon, Statistic stat) =>
|
|
pokemon.ReceivedCalls().Where(c => c.GetMethodInfo().Name == "ChangeStatBoost").Select(c => c.GetArguments())
|
|
.FirstOrDefault(args => (Statistic)args[0]! == stat);
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Memento causes the user to faint and in return lowers the Attack and Special Attack stat
|
|
/// of the target by two stages each."
|
|
/// The Attack drop is caused by the opponent's move, so it is not self-inflicted and not forced.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Hit_LowersTargetAttackByTwoStages()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var args = GetStatBoostArgs(target, Statistic.Attack);
|
|
await Assert.That(args).IsNotNull();
|
|
await Assert.That((sbyte)args![1]!).IsEqualTo((sbyte)-2);
|
|
await Assert.That((bool)args[2]!).IsFalse();
|
|
await Assert.That((bool)args[3]!).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Memento causes the user to faint and in return lowers the Attack and Special Attack stat
|
|
/// of the target by two stages each."
|
|
/// The Special Attack drop is caused by the opponent's move, so it is not self-inflicted and not forced.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Hit_LowersTargetSpecialAttackByTwoStages()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var args = GetStatBoostArgs(target, Statistic.SpecialAttack);
|
|
await Assert.That(args).IsNotNull();
|
|
await Assert.That((sbyte)args![1]!).IsEqualTo((sbyte)-2);
|
|
await Assert.That((bool)args[2]!).IsFalse();
|
|
await Assert.That((bool)args[3]!).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Memento causes the user to faint and in return lowers the Attack and Special Attack stat
|
|
/// of the target by two stages each."
|
|
/// Only those two stats are lowered; no other stat changes are requested.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Hit_OnlyLowersAttackAndSpecialAttack()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var statBoostCalls = target.ReceivedCalls().Count(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
|
await Assert.That(statBoostCalls).IsEqualTo(2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Memento causes the user to faint".
|
|
/// The faint is not move damage, so it uses <see cref="DamageSource.Misc"/>.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Hit_UserFaints()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, user) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var faintCall = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Faint");
|
|
await Assert.That(faintCall).IsNotNull();
|
|
await Assert.That((DamageSource)faintCall!.GetArguments()[0]!).IsEqualTo(DamageSource.Misc);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia (Generation V onward): "the user faints if it successfully hits but cannot lower stats due
|
|
/// to abilities like Clear Body or stats already at -6."
|
|
/// A substitute's <see cref="IBattlePokemon.ChangeStatBoost"/> returns false by default (the stat drops were
|
|
/// prevented), but the user must still faint.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_StatDropsPrevented_UserStillFaints()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, user) = CreateTestSetup();
|
|
|
|
// Act - the unconfigured ChangeStatBoost calls return false, as if blocked by Clear Body
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Faint")).IsTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: both stat drops are batched under the same <see cref="EventBatchId"/> so they are
|
|
/// shown as a single event to the client.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_Hit_StatDropsShareEventBatch()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, _) = CreateTestSetup();
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
var attackBatch = (EventBatchId)GetStatBoostArgs(target, Statistic.Attack)![4]!;
|
|
var specialAttackBatch = (EventBatchId)GetStatBoostArgs(target, Statistic.SpecialAttack)![4]!;
|
|
await Assert.That(attackBatch).IsEqualTo(specialAttackBatch);
|
|
}
|
|
} |