195 lines
7.8 KiB
C#
195 lines
7.8 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.Models.Choices;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Static.Moves;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Mimic"/> script, which implements Mimic.
|
|
/// Behavior is verified against the Bulbapedia page for Mimic (Generation VII).
|
|
/// The script is expected to copy the target's last used move through the engine's
|
|
/// <see cref="IScriptOnSecondaryEffect"/> hook, the same pattern used by <see cref="Sketch"/>.
|
|
/// </summary>
|
|
public class MimicTests
|
|
{
|
|
/// <summary>
|
|
/// Creates a mocked <see cref="ILearnedMove"/> whose move data has the given name.
|
|
/// </summary>
|
|
private static ILearnedMove CreateLearnedMove(string name)
|
|
{
|
|
var learned = Substitute.For<ILearnedMove>();
|
|
var data = Substitute.For<IMoveData>();
|
|
data.Name.Returns(new StringKey(name));
|
|
learned.MoveData.Returns(data);
|
|
return learned;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a fully mocked executing Mimic move. The user knows one other move in slot 0 and Mimic in
|
|
/// slot 1. If <paramref name="targetLastUsedMove"/> is null, the target has not used a move yet.
|
|
/// </summary>
|
|
private static (IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData) CreateTestSetup(
|
|
string? targetLastUsedMove, string userOtherMove = "swords_dance")
|
|
{
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var user = Substitute.For<IPokemon>();
|
|
var target = Substitute.For<IPokemon>();
|
|
move.User.Returns(user);
|
|
var hitData = Substitute.For<IHitData>();
|
|
move.GetHitData(target, 0).Returns(hitData);
|
|
|
|
// The user knows another move in slot 0, and Mimic in slot 1; Mimic is the chosen move.
|
|
// The moves are created before the Returns call, as creating substitutes inside Returns arguments
|
|
// resets NSubstitute's last-call tracking.
|
|
var mimicMove = CreateLearnedMove("mimic");
|
|
move.ChosenMove.Returns(mimicMove);
|
|
var userMoves = new[] { CreateLearnedMove(userOtherMove), mimicMove };
|
|
user.Moves.Returns(userMoves);
|
|
|
|
var battleData = Substitute.For<IPokemonBattleData>();
|
|
if (targetLastUsedMove != null)
|
|
{
|
|
var lastChoice = Substitute.For<IMoveChoice>();
|
|
var lastUsedMove = CreateLearnedMove(targetLastUsedMove);
|
|
lastChoice.ChosenMove.Returns(lastUsedMove);
|
|
battleData.LastMoveChoice.Returns(lastChoice);
|
|
}
|
|
else
|
|
{
|
|
battleData.LastMoveChoice.Returns((IMoveChoice?)null);
|
|
}
|
|
|
|
target.BattleData.Returns(battleData);
|
|
return (move, user, target, hitData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that the <see cref="Mimic"/> script implements the secondary effect hook, and returns it as
|
|
/// that hook. Every behavior of Mimic requires this hook to exist at all.
|
|
/// </summary>
|
|
private static async Task<IScriptOnSecondaryEffect> GetSecondaryEffectHook()
|
|
{
|
|
var mimic = new Mimic();
|
|
await Assert.That(mimic is IScriptOnSecondaryEffect).IsTrue();
|
|
return (IScriptOnSecondaryEffect)(object)mimic;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper that checks whether <see cref="IHitData.Fail"/> was called on the hit data.
|
|
/// </summary>
|
|
private static bool ReceivedFail(IHitData hitData) =>
|
|
hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail");
|
|
|
|
/// <summary>
|
|
/// Helper to extract the arguments of the user's received <see cref="IPokemon.LearnTemporaryMove"/> call.
|
|
/// </summary>
|
|
private static (StringKey moveName, byte index)? GetLearnedMove(IPokemon user)
|
|
{
|
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "LearnTemporaryMove");
|
|
if (call == null)
|
|
return null;
|
|
return ((StringKey)call.GetArguments()[0]!, (byte)call.GetArguments()[2]!);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Mimic copies a move from the target. The user will retain the copied move in Mimic's
|
|
/// place" and (Generation II onwards) "Mimic now copies the target's last used move."
|
|
/// The target's last used move must be learned in the move slot that holds Mimic.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetUsedMove_UserLearnsTargetsLastMoveInMimicsSlot()
|
|
{
|
|
// Arrange
|
|
var mimic = await GetSecondaryEffectHook();
|
|
var (move, user, target, _) = CreateTestSetup("tackle");
|
|
|
|
// Act
|
|
mimic.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert - the copied move replaces Mimic, which sits in slot 1.
|
|
var learned = GetLearnedMove(user);
|
|
await Assert.That(learned.HasValue).IsTrue();
|
|
await Assert.That(learned!.Value.moveName).IsEqualTo(new StringKey("tackle"));
|
|
await Assert.That(learned.Value.index).IsEqualTo((byte)1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: (Generation II onwards) "Mimic now copies the target's last used move."
|
|
/// If the target has not used a move yet, there is nothing to copy and the hit must fail.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetHasNotUsedAMove_FailsHit()
|
|
{
|
|
// Arrange
|
|
var mimic = await GetSecondaryEffectHook();
|
|
var (move, user, target, hitData) = CreateTestSetup(null);
|
|
|
|
// Act
|
|
mimic.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(ReceivedFail(hitData)).IsTrue();
|
|
await Assert.That(GetLearnedMove(user).HasValue).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: (Generation II onwards) "It now fails to copy the moves Sketch, Transform, Struggle,
|
|
/// Metronome, or any move the user already knows.", (Generation IV onwards) "Mimic will fail to copy
|
|
/// Chatter.", and (Generation V onwards) "Mimic is no longer able to copy Transform."
|
|
/// </summary>
|
|
[Test, Arguments("sketch"), Arguments("transform"), Arguments("struggle"), Arguments("metronome"),
|
|
Arguments("chatter")]
|
|
public async Task OnSecondaryEffect_TargetsLastMoveIsUncopyable_FailsHit(string uncopyableMove)
|
|
{
|
|
// Arrange
|
|
var mimic = await GetSecondaryEffectHook();
|
|
var (move, user, target, hitData) = CreateTestSetup(uncopyableMove);
|
|
|
|
// Act
|
|
mimic.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(ReceivedFail(hitData)).IsTrue();
|
|
await Assert.That(GetLearnedMove(user).HasValue).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: (Generation II onwards) "It now fails to copy ... any move the user already knows."
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_UserAlreadyKnowsTargetsLastMove_FailsHit()
|
|
{
|
|
// Arrange
|
|
var mimic = await GetSecondaryEffectHook();
|
|
// The target's last used move is Tackle, which the user already knows in slot 0.
|
|
var (move, user, target, hitData) = CreateTestSetup("tackle", "tackle");
|
|
|
|
// Act
|
|
mimic.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(ReceivedFail(hitData)).IsTrue();
|
|
await Assert.That(GetLearnedMove(user).HasValue).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: (Generation II onwards) "The copied move will also only have 5 PP".
|
|
/// After learning the copy, its current PP must be set to 5.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetUsedMove_CopiedMoveGetsFivePP()
|
|
{
|
|
// Arrange
|
|
var mimic = await GetSecondaryEffectHook();
|
|
var (move, user, target, _) = CreateTestSetup("tackle");
|
|
|
|
// Act
|
|
mimic.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert - the move in Mimic's slot (slot 1) has its PP set to 5.
|
|
user.Moves[1]!.Received(1).SetCurrentPP(5);
|
|
}
|
|
} |