Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/MagnetRiseTests.cs
Deukhoofd d2a82b5fe3
All checks were successful
Build / Build (push) Successful in 3m12s
Many more tests and fixes
2026-08-27 17:11:12 +02:00

206 lines
7.7 KiB
C#

using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
using PkmnLib.Static;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Species;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="MagnetRise"/> move script and its <see cref="MagnetRiseEffect"/> volatile.
/// Gen VII Bulbapedia behavior: "The user becomes immune to Ground-type attacks for five turns." "Magnet Rise
/// fails if the user is under the effects of Ingrain", "If the user is holding an Iron Ball, Magnet Rise can
/// still be used, but the user remains on the ground", and (Generation V onwards) "If used again, Magnet Rise
/// fails."
/// The Ground immunity itself is implemented by the <see cref="MagnetRiseEffect"/> volatile; the move script
/// is responsible for applying that volatile to the user.
/// </summary>
public class MagnetRiseTests
{
/// <summary>
/// Creates a fully mocked test setup for Magnet Rise tests. The user gets a real
/// <see cref="ScriptSet"/> as its volatile set, so the applied effect can be inspected.
/// </summary>
private static (MagnetRise script, IExecutingMove move, IPokemon target, IPokemon user, IScriptSet userVolatile,
IHitData hitData) CreateTestSetup()
{
var script = new MagnetRise();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var hitData = Substitute.For<IHitData>();
move.GetHitData(target, 0).Returns(hitData);
var user = Substitute.For<IPokemon>();
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
user.ActiveAbility.Returns((IAbility?)null);
var userVolatile = new ScriptSet(user);
user.Volatile.Returns(userVolatile);
move.User.Returns(user);
return (script, move, target, user, userVolatile, hitData);
}
/// <summary>
/// Helper that checks whether the hit was failed.
/// </summary>
private static bool HitWasFailed(IHitData hitData) =>
hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail");
/// <summary>
/// Bulbapedia: "The user becomes immune to Ground-type attacks for five turns."
/// The move script applies the <see cref="MagnetRiseEffect"/> volatile, which implements the immunity,
/// to the user, and the move does not fail.
/// </summary>
[Test]
public async Task OnSecondaryEffect_MoveUsed_AddsMagnetRiseEffectToUserVolatile()
{
// Arrange
var (script, move, target, _, userVolatile, hitData) = CreateTestSetup();
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(userVolatile.Get<MagnetRiseEffect>()).IsNotNull();
await Assert.That(HitWasFailed(hitData)).IsFalse();
}
/// <summary>
/// Bulbapedia: "Magnet Rise fails if the user is under the effects of Ingrain". When the user has the
/// <see cref="IngrainEffect"/> volatile, the hit is failed and no <see cref="MagnetRiseEffect"/> is
/// applied.
/// </summary>
[Test]
public async Task OnSecondaryEffect_UserUnderIngrain_MoveFails()
{
// Arrange
var (script, move, target, user, userVolatile, hitData) = CreateTestSetup();
userVolatile.Add(new IngrainEffect(user));
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(HitWasFailed(hitData)).IsTrue();
await Assert.That(userVolatile.Contains("magnet_rise")).IsFalse();
}
/// <summary>
/// Bulbapedia (Generation V onwards): "If used again, Magnet Rise fails." Using the move while its
/// effect is already active must fail the hit.
/// </summary>
[Test]
public async Task OnSecondaryEffect_UsedWhileAlreadyActive_MoveFails()
{
// Arrange
var (script, move, target, _, _, hitData) = CreateTestSetup();
// Act - second use while the effect is active
script.OnSecondaryEffect(move, target, 0);
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(HitWasFailed(hitData)).IsTrue();
}
// ----- MagnetRiseEffect behavior (the volatile the move applies) -----
/// <summary>
/// Creates an incoming executing move of the given type, aimed at the given target.
/// </summary>
private static IExecutingMove CreateIncomingMove(string moveType)
{
var move = Substitute.For<IExecutingMove>();
var moveData = Substitute.For<IMoveData>();
moveData.MoveType.Returns(new TypeIdentifier(5, moveType));
move.UseMove.Returns(moveData);
return move;
}
/// <summary>
/// Bulbapedia: "The user becomes immune to Ground-type attacks for five turns." While the effect is
/// active, the effectiveness of incoming Ground-type moves against the user becomes 0.
/// </summary>
[Test]
public async Task ChangeEffectiveness_IncomingGroundMove_UserIsImmune()
{
// Arrange
var effect = new MagnetRiseEffect();
var incomingMove = CreateIncomingMove("ground");
var owner = Substitute.For<IPokemon>();
var effectiveness = 1.0f;
// Act
effect.ChangeEffectiveness(incomingMove, owner, 0, ref effectiveness);
// Assert
await Assert.That(effectiveness).IsEqualTo(0.0f);
}
/// <summary>
/// Bulbapedia: the user only becomes "immune to Ground-type attacks"; moves of other types are
/// unaffected by Magnet Rise.
/// </summary>
[Test]
public async Task ChangeEffectiveness_IncomingNonGroundMove_EffectivenessUnchanged()
{
// Arrange
var effect = new MagnetRiseEffect();
var incomingMove = CreateIncomingMove("rock");
var owner = Substitute.For<IPokemon>();
var effectiveness = 2.0f;
// Act
effect.ChangeEffectiveness(incomingMove, owner, 0, ref effectiveness);
// Assert
await Assert.That(effectiveness).IsEqualTo(2.0f);
}
/// <summary>
/// Bulbapedia: "If the user is holding an Iron Ball, Magnet Rise can still be used, but the user remains
/// on the ground until the item is removed from the user." A user holding an Iron Ball is not immune to
/// Ground-type moves despite the active effect.
/// </summary>
[Test]
public async Task ChangeEffectiveness_OwnerHoldingIronBall_GroundMovesStillHit()
{
// Arrange
var effect = new MagnetRiseEffect();
var incomingMove = CreateIncomingMove("ground");
var owner = Substitute.For<IPokemon>();
owner.HasHeldItem("iron_ball").Returns(true);
incomingMove.User.Returns(owner);
var effectiveness = 1.0f;
// Act
effect.ChangeEffectiveness(incomingMove, owner, 0, ref effectiveness);
// Assert
await Assert.That(effectiveness).IsEqualTo(1.0f);
}
/// <summary>
/// Bulbapedia: "The user becomes immune to Ground-type attacks for five turns." The turn Magnet Rise is
/// used counts as the first turn, so after five end-of-turn ticks the effect must have removed itself
/// from the user.
/// </summary>
[Test]
public async Task OnEndTurn_AfterFiveEndOfTurns_EffectHasEnded()
{
// Arrange
var (script, move, target, user, userVolatile, _) = CreateTestSetup();
script.OnSecondaryEffect(move, target, 0);
var effect = userVolatile.Get<MagnetRiseEffect>()!;
var battle = Substitute.For<IBattle>();
// Act - five end-of-turn ticks, the duration of the effect
for (var i = 0; i < 5; i++)
effect.OnEndTurn(user, battle);
// Assert
await Assert.That(userVolatile.Contains("magnet_rise")).IsFalse();
}
}