This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
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="Acupressure"/> move script.
|
||||
/// Gen VII Bulbapedia behavior: "Acupressure chooses one of the target's stats at random and raises it by
|
||||
/// two stages. It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
|
||||
/// accuracy, or evasion stat but will not attempt to raise a stat that is already maximized, meaning that
|
||||
/// the move will fail if all stats are maximized."
|
||||
/// </summary>
|
||||
public class AcupressureTests
|
||||
{
|
||||
private static (Acupressure script, IExecutingMove move, IPokemon target, IBattleRandom random, IHitData hitData)
|
||||
CreateTestSetup()
|
||||
{
|
||||
var script = new Acupressure();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var target = Substitute.For<IPokemon>();
|
||||
var hitData = Substitute.For<IHitData>();
|
||||
move.GetHitData(target, 0).Returns(hitData);
|
||||
|
||||
var random = Substitute.For<IBattleRandom>();
|
||||
var battle = Substitute.For<IBattle>();
|
||||
battle.Random.Returns(random);
|
||||
var battleData = Substitute.For<IPokemonBattleData>();
|
||||
battleData.Battle.Returns(battle);
|
||||
|
||||
var user = Substitute.For<IPokemon>();
|
||||
user.BattleData.Returns(battleData);
|
||||
move.User.Returns(user);
|
||||
|
||||
target.StatBoost.Returns(new StatBoostStatisticSet());
|
||||
|
||||
return (script, move, target, random, hitData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to extract the stat argument of a ChangeStatBoost call received by the target.
|
||||
/// </summary>
|
||||
private static object?[]? GetStatBoostCallArgs(IPokemon target)
|
||||
{
|
||||
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||
return call?.GetArguments();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Acupressure chooses one of the target's stats at random and raises it by two stages."
|
||||
/// When the random roll selects Attack, the target's Attack is raised by exactly two stages.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_RandomSelectsAttack_RaisesAttackByTwoStages()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, random, _) = CreateTestSetup();
|
||||
// Attack is the first entry in the pool of raisable stats (HP is excluded).
|
||||
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(0);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
var args = GetStatBoostCallArgs(target);
|
||||
await Assert.That(args).IsNotNull();
|
||||
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.Attack);
|
||||
await Assert.That((sbyte)args[1]!).IsEqualTo((sbyte)2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
|
||||
/// accuracy, or evasion stat" — HP is not one of the raisable stats. When the random roll returns the lowest
|
||||
/// value of its range, the selected stat must be Attack, not HP.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_RandomMinimumRoll_SelectsAttackNotHp()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, random, _) = CreateTestSetup();
|
||||
// Return the inclusive lower bound of whatever range is requested.
|
||||
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(ci => ci.ArgAt<int>(0));
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
var args = GetStatBoostCallArgs(target);
|
||||
await Assert.That(args).IsNotNull();
|
||||
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.Attack);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
|
||||
/// accuracy, or evasion stat". Accuracy and evasion must be selectable: when the random roll returns the
|
||||
/// highest value of its range, the selected stat should be one of the stats beyond Speed in the
|
||||
/// <see cref="Statistic"/> enum (Evasion or Accuracy).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_RandomMaximumRoll_CanSelectAccuracyOrEvasion()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, random, _) = CreateTestSetup();
|
||||
// Return the highest value of whatever range is requested (upper bound is exclusive).
|
||||
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(ci => ci.ArgAt<int>(1) - 1);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert - the highest selectable stat should be Evasion or Accuracy
|
||||
var args = GetStatBoostCallArgs(target);
|
||||
await Assert.That(args).IsNotNull();
|
||||
var stat = (Statistic)args![0]!;
|
||||
await Assert.That(stat is Statistic.Evasion or Statistic.Accuracy).IsTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "the move will fail if all stats are maximized." All raisable stats (Attack through accuracy;
|
||||
/// HP boosts cannot be raised by any move and stay at 0) are at +6, so the move must fail without attempting
|
||||
/// a stat change.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_AllRaisableStatsMaximized_Fails()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, _, hitData) = CreateTestSetup();
|
||||
var statBoost = new StatBoostStatisticSet(0, 6, 6, 6, 6, 6) { Evasion = 6, Accuracy = 6 };
|
||||
target.StatBoost.Returns(statBoost);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost")).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "will not attempt to raise a stat that is already maximized". With the target's Attack at +6
|
||||
/// and other stats available, Attack must never be the raised stat, regardless of what the random roll
|
||||
/// returns. The rolls cover every index of the six-stat selection pool that remains.
|
||||
/// </summary>
|
||||
[Test, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5)]
|
||||
public async Task OnSecondaryEffect_StatAlreadyMaximized_NeverRaisesThatStat(int roll)
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, random, _) = CreateTestSetup();
|
||||
var statBoost = new StatBoostStatisticSet(0, 6, 0, 0, 0, 0);
|
||||
target.StatBoost.Returns(statBoost);
|
||||
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(roll);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert - a stat is raised, but never the maximized Attack
|
||||
var args = GetStatBoostCallArgs(target);
|
||||
await Assert.That(args).IsNotNull();
|
||||
await Assert.That((Statistic)args![0]!).IsNotEqualTo(Statistic.Attack);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user