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; /// /// Tests for the move script and its 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 volatile; the move script /// is responsible for applying that volatile to the user. /// public class MagnetRiseTests { /// /// Creates a fully mocked test setup for Magnet Rise tests. The user gets a real /// as its volatile set, so the applied effect can be inspected. /// private static (MagnetRise script, IExecutingMove move, IBattlePokemon target, IBattlePokemon user, IScriptSet userVolatile, IHitData hitData) CreateTestSetup() { var script = new MagnetRise(); var move = Substitute.For(); var target = Substitute.For(); var hitData = Substitute.For(); move.GetHitData(target, 0).Returns(hitData); var user = Substitute.For(); user.GetScripts().Returns(_ => new ScriptIterator(new List>())); 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); } /// /// Helper that checks whether the hit was failed. /// private static bool HitWasFailed(IHitData hitData) => hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail"); /// /// Bulbapedia: "The user becomes immune to Ground-type attacks for five turns." /// The move script applies the volatile, which implements the immunity, /// to the user, and the move does not fail. /// [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()).IsNotNull(); await Assert.That(HitWasFailed(hitData)).IsFalse(); } /// /// Bulbapedia: "Magnet Rise fails if the user is under the effects of Ingrain". When the user has the /// volatile, the hit is failed and no is /// applied. /// [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(); } /// /// Bulbapedia (Generation V onwards): "If used again, Magnet Rise fails." Using the move while its /// effect is already active must fail the hit. /// [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) ----- /// /// Creates an incoming executing move of the given type, aimed at the given target. /// private static IExecutingMove CreateIncomingMove(string moveType) { var move = Substitute.For(); var moveData = Substitute.For(); moveData.MoveType.Returns(new TypeIdentifier(5, moveType)); move.UseMove.Returns(moveData); return move; } /// /// 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. /// [Test] public async Task ChangeEffectiveness_IncomingGroundMove_UserIsImmune() { // Arrange var effect = new MagnetRiseEffect(); var incomingMove = CreateIncomingMove("ground"); var owner = Substitute.For(); var effectiveness = 1.0f; // Act effect.ChangeEffectiveness(incomingMove, owner, 0, ref effectiveness); // Assert await Assert.That(effectiveness).IsEqualTo(0.0f); } /// /// Bulbapedia: the user only becomes "immune to Ground-type attacks"; moves of other types are /// unaffected by Magnet Rise. /// [Test] public async Task ChangeEffectiveness_IncomingNonGroundMove_EffectivenessUnchanged() { // Arrange var effect = new MagnetRiseEffect(); var incomingMove = CreateIncomingMove("rock"); var owner = Substitute.For(); var effectiveness = 2.0f; // Act effect.ChangeEffectiveness(incomingMove, owner, 0, ref effectiveness); // Assert await Assert.That(effectiveness).IsEqualTo(2.0f); } /// /// 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. /// [Test] public async Task ChangeEffectiveness_OwnerHoldingIronBall_GroundMovesStillHit() { // Arrange var effect = new MagnetRiseEffect(); var incomingMove = CreateIncomingMove("ground"); var owner = Substitute.For(); 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); } /// /// 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. /// [Test] public async Task OnEndTurn_AfterFiveEndOfTurns_EffectHasEnded() { // Arrange var (script, move, target, user, userVolatile, _) = CreateTestSetup(); script.OnSecondaryEffect(move, target, 0); var effect = userVolatile.Get()!; var battle = Substitute.For(); // 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(); } }