This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
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="HeartSwap"/> move script.
|
||||
/// Gen VII Bulbapedia behavior: "Heart Swap switches the user's stat stages with the target's stat
|
||||
/// stages."
|
||||
/// </summary>
|
||||
public class HeartSwapTests
|
||||
{
|
||||
private static (HeartSwap script, IExecutingMove move, IPokemon user, IPokemon target) CreateTestSetup(
|
||||
StatBoostStatisticSet userBoosts, StatBoostStatisticSet targetBoosts)
|
||||
{
|
||||
var script = new HeartSwap();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
|
||||
var user = Substitute.For<IPokemon>();
|
||||
user.StatBoost.Returns(userBoosts);
|
||||
move.User.Returns(user);
|
||||
|
||||
var target = Substitute.For<IPokemon>();
|
||||
target.StatBoost.Returns(targetBoosts);
|
||||
|
||||
return (script, move, user, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to extract the arguments of the received ChangeStatBoost call for the given statistic, or
|
||||
/// null when that stat was not changed. Received-call inspection is used instead of NSubstitute
|
||||
/// argument matchers because the trailing EventBatchId parameter cannot be bound by <c>Arg.Any</c>.
|
||||
/// </summary>
|
||||
private static object?[]? GetStatBoostArgs(IPokemon pokemon, Statistic stat) =>
|
||||
pokemon.ReceivedCalls().Where(c => c.GetMethodInfo().Name == "ChangeStatBoost").Select(c => c.GetArguments())
|
||||
.FirstOrDefault(args => (Statistic)args[0]! == stat);
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Heart Swap switches the user's stat stages with the target's stat stages." The user
|
||||
/// must end up at the target's stage and vice versa, so the user is changed by
|
||||
/// (target - user) and the target by (user - target).
|
||||
/// </summary>
|
||||
[Test, Arguments((sbyte)2, (sbyte)-3, (sbyte)-5, (sbyte)5), Arguments((sbyte)-1, (sbyte)4, (sbyte)5, (sbyte)-5),
|
||||
Arguments((sbyte)6, (sbyte)-6, (sbyte)-12, (sbyte)12), Arguments((sbyte)0, (sbyte)1, (sbyte)1, (sbyte)-1)]
|
||||
public async Task OnSecondaryEffect_DifferentAttackStages_StagesAreSwapped(sbyte userAttack, sbyte targetAttack,
|
||||
sbyte expectedUserChange, sbyte expectedTargetChange)
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, target) = CreateTestSetup(new StatBoostStatisticSet(0, userAttack, 0, 0, 0, 0),
|
||||
new StatBoostStatisticSet(0, targetAttack, 0, 0, 0, 0));
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
var userArgs = GetStatBoostArgs(user, Statistic.Attack);
|
||||
var targetArgs = GetStatBoostArgs(target, Statistic.Attack);
|
||||
await Assert.That(userArgs).IsNotNull();
|
||||
await Assert.That((sbyte)userArgs![1]!).IsEqualTo(expectedUserChange);
|
||||
await Assert.That(targetArgs).IsNotNull();
|
||||
await Assert.That((sbyte)targetArgs![1]!).IsEqualTo(expectedTargetChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Heart Swap switches the user's stat stages with the target's stat stages." — every
|
||||
/// battle stat stage (Attack, Defense, Special Attack, Special Defense, Speed) is exchanged at once.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_AllBattleStatStages_AreSwapped()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, target) = CreateTestSetup(new StatBoostStatisticSet(0, 2, -1, 3, -4, 6),
|
||||
new StatBoostStatisticSet(0, -2, 4, 0, 1, -6));
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert - each user change is (target - user), each target change is (user - target)
|
||||
foreach (var (stat, userStage, targetStage) in new[]
|
||||
{
|
||||
(Statistic.Attack, (sbyte)2, (sbyte)-2),
|
||||
(Statistic.Defense, (sbyte)-1, (sbyte)4),
|
||||
(Statistic.SpecialAttack, (sbyte)3, (sbyte)0),
|
||||
(Statistic.SpecialDefense, (sbyte)-4, (sbyte)1),
|
||||
(Statistic.Speed, (sbyte)6, (sbyte)-6),
|
||||
})
|
||||
{
|
||||
var userArgs = GetStatBoostArgs(user, stat);
|
||||
var targetArgs = GetStatBoostArgs(target, stat);
|
||||
await Assert.That(userArgs).IsNotNull();
|
||||
await Assert.That((sbyte)userArgs![1]!).IsEqualTo((sbyte)(targetStage - userStage));
|
||||
await Assert.That(targetArgs).IsNotNull();
|
||||
await Assert.That((sbyte)targetArgs![1]!).IsEqualTo((sbyte)(userStage - targetStage));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Heart Swap switches the user's stat stages with the target's stat stages." — stat
|
||||
/// stages include <see cref="Statistic.Evasion"/> and <see cref="Statistic.Accuracy"/>, which are
|
||||
/// exchanged as well.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_EvasionAndAccuracyStages_AreSwapped()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, target) = CreateTestSetup(
|
||||
new StatBoostStatisticSet(0, 0, 0, 0, 0, 0) { Evasion = 2, Accuracy = -1 },
|
||||
new StatBoostStatisticSet(0, 0, 0, 0, 0, 0) { Evasion = -3, Accuracy = 4 });
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
var userEvasionArgs = GetStatBoostArgs(user, Statistic.Evasion);
|
||||
var userAccuracyArgs = GetStatBoostArgs(user, Statistic.Accuracy);
|
||||
await Assert.That(userEvasionArgs).IsNotNull();
|
||||
await Assert.That((sbyte)userEvasionArgs![1]!).IsEqualTo((sbyte)-5);
|
||||
await Assert.That(userAccuracyArgs).IsNotNull();
|
||||
await Assert.That((sbyte)userAccuracyArgs![1]!).IsEqualTo((sbyte)5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: swapping identical stat stages is a net-zero exchange — neither Pokémon's stages are
|
||||
/// modified.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_EqualStages_NoStagesAreChanged()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, target) = CreateTestSetup(new StatBoostStatisticSet(0, 2, -1, 0, 3, 1),
|
||||
new StatBoostStatisticSet(0, 2, -1, 0, 3, 1));
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost")).IsFalse();
|
||||
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost")).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: the exchange is unconditional — the changes are applied with force so clamping or
|
||||
/// prevention effects cannot block the swap, and only the user's own change counts as
|
||||
/// self-inflicted.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_Always_ChangesAreForced()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, target) = CreateTestSetup(new StatBoostStatisticSet(0, 2, 0, 0, 0, 0),
|
||||
new StatBoostStatisticSet(0, -1, 0, 0, 0, 0));
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
var userArgs = GetStatBoostArgs(user, Statistic.Attack);
|
||||
var targetArgs = GetStatBoostArgs(target, Statistic.Attack);
|
||||
await Assert.That((bool)userArgs![3]!).IsTrue(); // forced
|
||||
await Assert.That((bool)targetArgs![3]!).IsTrue(); // forced
|
||||
await Assert.That((bool)userArgs[2]!).IsTrue(); // self-inflicted for the user
|
||||
await Assert.That((bool)targetArgs[2]!).IsFalse(); // not self-inflicted for the target
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user