This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="HelpingHand"/> move script and the <see cref="HelpingHandEffect"/> volatile script it
|
||||
/// applies to the ally.
|
||||
/// Gen VII Bulbapedia behavior: "Helping Hand is an increased priority move that will increase the damage done by
|
||||
/// the user's ally this turn by 50%. It will fail if there is no adjacent ally, or if the ally has already acted
|
||||
/// this turn." and "If Helping Hand is used multiple times in a turn, all targeting the same Pokémon, the boosts
|
||||
/// are cumulative."
|
||||
/// </summary>
|
||||
public class HelpingHandTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an ally with a real <see cref="ScriptSet"/> as its <see cref="IPokemon.Volatile"/> set, so that
|
||||
/// adding, stacking, and removing the <see cref="HelpingHandEffect"/> behaves as it would in battle.
|
||||
/// </summary>
|
||||
private static (IPokemon ally, ScriptSet volatiles) CreateAllyWithRealVolatileSet()
|
||||
{
|
||||
var ally = Substitute.For<IPokemon>();
|
||||
var volatiles = new ScriptSet(ally);
|
||||
// ScriptSet.Add runs the IScriptPreventVolatileAdd hook over the owner's scripts; give the mock a real
|
||||
// iterator so that hook pass runs (empty of blockers here).
|
||||
ally.GetScripts().Returns(_ => new ScriptIterator(new IEnumerable<ScriptContainer>[] { volatiles }));
|
||||
ally.Volatile.Returns(volatiles);
|
||||
return (ally, volatiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies <see cref="IScriptChangeBasePower.ChangeBasePower"/> of every script in the ally's volatile set to
|
||||
/// the given base power, as the battle engine would when the ally uses its move.
|
||||
/// </summary>
|
||||
private static ushort RunChangeBasePowerHooks(ScriptSet volatiles, ushort basePower)
|
||||
{
|
||||
var allyMove = Substitute.For<IExecutingMove>();
|
||||
var opponent = Substitute.For<IPokemon>();
|
||||
foreach (var container in volatiles)
|
||||
{
|
||||
if (container.Script is IScriptChangeBasePower changeBasePower)
|
||||
changeBasePower.ChangeBasePower(allyMove, opponent, 0, ref basePower);
|
||||
}
|
||||
|
||||
return basePower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Helping Hand is an increased priority move that will increase the damage done by the user's
|
||||
/// ally this turn by 50%." Using the move attaches the <see cref="HelpingHandEffect"/> volatile to the targeted
|
||||
/// ally.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_UsedOnAlly_AddsHelpingHandEffectToAlly()
|
||||
{
|
||||
// Arrange
|
||||
var helpingHand = new HelpingHand();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var ally = Substitute.For<IPokemon>();
|
||||
|
||||
// Act
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
|
||||
// Assert
|
||||
var addCall = ally.Volatile.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Add");
|
||||
await Assert.That(addCall).IsNotNull();
|
||||
await Assert.That(addCall!.GetArguments()[0] is HelpingHandEffect).IsTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: Helping Hand "will increase the damage done by the user's ally this turn by 50%."
|
||||
/// The <see cref="HelpingHandEffect"/> multiplies the boosted move's base power by 1.5, truncating fractions.
|
||||
/// </summary>
|
||||
[Test, Arguments((ushort)100, (ushort)150), Arguments((ushort)60, (ushort)90), Arguments((ushort)65, (ushort)97),
|
||||
Arguments((ushort)1, (ushort)1)]
|
||||
public async Task ChangeBasePower_EffectActive_BasePowerIncreasedByFiftyPercent(ushort basePower,
|
||||
ushort expectedPower)
|
||||
{
|
||||
// Arrange
|
||||
var effect = new HelpingHandEffect();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var target = Substitute.For<IPokemon>();
|
||||
|
||||
// Act
|
||||
effect.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo(expectedPower);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Technical test: boosting a very large base power clamps at <see cref="ushort.MaxValue"/> instead of
|
||||
/// overflowing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_LargeBasePower_ClampsAtMaxValue()
|
||||
{
|
||||
// Arrange
|
||||
var effect = new HelpingHandEffect();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var target = Substitute.For<IPokemon>();
|
||||
var basePower = ushort.MaxValue;
|
||||
|
||||
// Act
|
||||
effect.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo(ushort.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "If Helping Hand is used multiple times in a turn, all targeting the same Pokémon, the boosts
|
||||
/// are cumulative. The move power will increase by 125% if there are two Helping Hands".
|
||||
/// Two Helping Hands on the same ally should therefore boost 100 base power to 225 (1.5 × 1.5).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_TwoHelpingHandsOnSameAlly_BoostsAreCumulative()
|
||||
{
|
||||
// Arrange
|
||||
var helpingHand = new HelpingHand();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var (ally, volatiles) = CreateAllyWithRealVolatileSet();
|
||||
|
||||
// Act - two different Pokémon use Helping Hand on the same ally in the same turn
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
var boosted = RunChangeBasePowerHooks(volatiles, 100);
|
||||
|
||||
// Assert - 100 × 1.5 × 1.5 = 225
|
||||
await Assert.That(boosted).IsEqualTo((ushort)225);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "The move power will increase by ... 237.5% if there are three [Helping Hands]."
|
||||
/// Three Helping Hands on the same ally should boost 100 base power to 337 (1.5³ = 3.375, truncated).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_ThreeHelpingHandsOnSameAlly_BoostsAreCumulative()
|
||||
{
|
||||
// Arrange
|
||||
var helpingHand = new HelpingHand();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var (ally, volatiles) = CreateAllyWithRealVolatileSet();
|
||||
|
||||
// Act
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
var boosted = RunChangeBasePowerHooks(volatiles, 100);
|
||||
|
||||
// Assert - 100 × 1.5 × 1.5 × 1.5 = 337.5, truncated to 337
|
||||
await Assert.That(boosted).IsEqualTo((ushort)337);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: Helping Hand increases "the damage done by the user's ally this turn" — the boost only lasts
|
||||
/// for the turn it was used, so the <see cref="HelpingHandEffect"/> removes itself at the end of the turn.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnEndTurn_TurnEnds_EffectRemovesItselfFromAlly()
|
||||
{
|
||||
// Arrange
|
||||
var (ally, volatiles) = CreateAllyWithRealVolatileSet();
|
||||
var helpingHand = new HelpingHand();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
await Assert.That(volatiles.TryGet<HelpingHandEffect>(out var effect)).IsTrue();
|
||||
|
||||
// Act
|
||||
effect!.OnEndTurn(ally, Substitute.For<IBattle>());
|
||||
|
||||
// Assert
|
||||
await Assert.That(volatiles.TryGet<HelpingHandEffect>(out _)).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: the 50% boost applies "this turn" — after the effect has been removed at the end of the turn,
|
||||
/// the ally's moves are no longer boosted.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_AfterEndOfTurn_BasePowerNoLongerBoosted()
|
||||
{
|
||||
// Arrange
|
||||
var (ally, volatiles) = CreateAllyWithRealVolatileSet();
|
||||
var helpingHand = new HelpingHand();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
helpingHand.OnSecondaryEffect(move, ally, 0);
|
||||
volatiles.TryGet<HelpingHandEffect>(out var effect);
|
||||
|
||||
// Act
|
||||
effect!.OnEndTurn(ally, Substitute.For<IBattle>());
|
||||
var boosted = RunChangeBasePowerHooks(volatiles, 100);
|
||||
|
||||
// Assert
|
||||
await Assert.That(boosted).IsEqualTo((ushort)100);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user