Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/AssistTests.cs
Deukhoofd f9878e76ba
All checks were successful
Build / Build (push) Successful in 1m7s
Adds more tests, fixes
2026-07-04 13:03:54 +02:00

205 lines
7.2 KiB
C#

using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
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="Assist"/> move script.
/// Gen VII Bulbapedia behavior: "The Assist user executes a move that is randomly selected from all the
/// eligible moves known by other Pokémon in the same party. If the same move is known by multiple Pokémon,
/// each of them counts [...]. Assist can call a move that currently has no PP or is known by a fainted
/// Pokémon. [...] If the other Pokémon in the user's party do not know any eligible moves, Assist fails."
/// </summary>
public class AssistTests
{
private static IPokemon CreatePokemonWithMoves(params string[] moveNames)
{
var pokemon = Substitute.For<IPokemon>();
var moves = moveNames.Select(name =>
{
var learned = Substitute.For<ILearnedMove>();
var data = Substitute.For<IMoveData>();
data.Name.Returns(new StringKey(name));
learned.MoveData.Returns(data);
return (ILearnedMove?)learned;
}).ToArray();
pokemon.Moves.Returns(moves);
return pokemon;
}
private static (Assist script, IMoveChoice choice, IPokemon user, IBattleRandom random) CreateTestSetup(
params IPokemon?[] otherPartyMembers)
{
var script = new Assist();
var user = CreatePokemonWithMoves("tackle");
var members = new List<IPokemon?> { user };
members.AddRange(otherPartyMembers);
var party = Substitute.For<IPokemonParty>();
party.GetEnumerator().Returns(_ => members.GetEnumerator());
var battleParty = Substitute.For<IBattleParty>();
battleParty.Party.Returns(party);
var random = Substitute.For<IBattleRandom>();
var battle = Substitute.For<IBattle>();
battle.Parties.Returns(new[] { battleParty });
battle.Random.Returns(random);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
user.BattleData.Returns(battleData);
var choice = Substitute.For<IMoveChoice>();
choice.User.Returns(user);
return (script, choice, user, random);
}
/// <summary>
/// Bulbapedia: "The Assist user executes a move that is randomly selected from all the eligible moves known
/// by other Pokémon in the same party."
/// </summary>
[Test]
public async Task ChangeMove_AllyKnowsEligibleMove_SelectsThatMove()
{
// Arrange
var (script, choice, _, random) = CreateTestSetup(CreatePokemonWithMoves("growl"));
random.GetInt(Arg.Any<int>()).Returns(0);
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("growl"));
choice.DidNotReceive().Fail();
}
/// <summary>
/// Bulbapedia: "If the other Pokémon in the user's party do not know any eligible moves, Assist fails."
/// The user's own moves are not eligible: with no other party members, the move fails even though the
/// user itself knows a copyable move.
/// </summary>
[Test]
public async Task ChangeMove_NoOtherPartyMembers_Fails()
{
// Arrange
var (script, choice, _, _) = CreateTestSetup();
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
choice.Received(1).Fail();
await Assert.That(moveName).IsEqualTo(new StringKey("assist"));
}
/// <summary>
/// Bulbapedia lists moves that Assist cannot call (e.g. Protect, Counter, moves with a semi-invulnerable
/// turn). An ally that only knows ineligible moves provides no options, so Assist fails.
/// </summary>
[Test]
public async Task ChangeMove_AllyOnlyKnowsIneligibleMoves_Fails()
{
// Arrange
var (script, choice, _, _) = CreateTestSetup(CreatePokemonWithMoves("protect", "counter", "dig"));
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
choice.Received(1).Fail();
await Assert.That(moveName).IsEqualTo(new StringKey("assist"));
}
/// <summary>
/// Bulbapedia: "If the same move is known by multiple Pokémon, each of them counts and therefore the move
/// is more likely to be called." Two allies knowing Tackle plus one knowing Growl yields three entries in
/// the random selection pool.
/// </summary>
[Test]
public void ChangeMove_SameMoveKnownByMultipleAllies_EachCounts()
{
// Arrange
var (script, choice, _, random) = CreateTestSetup(CreatePokemonWithMoves("pound"),
CreatePokemonWithMoves("pound"), CreatePokemonWithMoves("growl"));
random.GetInt(Arg.Any<int>()).Returns(0);
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert - the selection pool contains three moves (pound twice, growl once)
random.Received(1).GetInt(3);
choice.DidNotReceive().Fail();
}
/// <summary>
/// Bulbapedia: "Assist can call a move that [...] is known by a fainted Pokémon."
/// </summary>
[Test]
public async Task ChangeMove_AllyIsFainted_MoveStillEligible()
{
// Arrange
var faintedAlly = CreatePokemonWithMoves("growl");
faintedAlly.IsUsable.Returns(false);
var (script, choice, _, random) = CreateTestSetup(faintedAlly);
random.GetInt(Arg.Any<int>()).Returns(0);
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("growl"));
choice.DidNotReceive().Fail();
}
/// <summary>
/// Bulbapedia: "Assist can call a move that currently has no PP".
/// </summary>
[Test]
public async Task ChangeMove_AllyMoveHasNoPp_MoveStillEligible()
{
// Arrange
var ally = CreatePokemonWithMoves("growl");
ally.Moves[0]!.CurrentPp.Returns((byte)0);
var (script, choice, _, random) = CreateTestSetup(ally);
random.GetInt(Arg.Any<int>()).Returns(0);
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("growl"));
choice.DidNotReceive().Fail();
}
/// <summary>
/// Technical test: outside of battle (no battle data) the script returns without failing the choice.
/// </summary>
[Test]
public async Task ChangeMove_NoBattleData_DoesNothing()
{
// Arrange
var script = new Assist();
var user = Substitute.For<IPokemon>();
user.BattleData.Returns((IPokemonBattleData?)null);
var choice = Substitute.For<IMoveChoice>();
choice.User.Returns(user);
StringKey moveName = "assist";
// Act
script.ChangeMove(choice, ref moveName);
// Assert
choice.DidNotReceive().Fail();
await Assert.That(moveName).IsEqualTo(new StringKey("assist"));
}
}