using PkmnLib.Dynamic.Events;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
///
/// Tests for the move script.
/// Gen VII Bulbapedia behavior: "Autotomize raises the user's Speed stat by two stages and (if the user
/// successfully changes its Speed) decreases its weight by 220 lbs. (100 kg). If the user successfully
/// changes its weight, the message '<Pokémon> became nimble!' is displayed. Autotomize cannot decrease
/// the user's weight below the minimum 0.2 lbs (0.1 kg); if the user's weight would drop below the minimum,
/// it becomes the minimum instead. Weight loss from Autotomize stacks".
///
public class AutotomizeTests
{
private static (Autotomize script, IExecutingMove move, IPokemon user, IScriptSet userVolatile, EventHook eventHook)
CreateTestSetup(float weightInKg, bool speedRaiseSucceeds, AutotomizeEffect? existingEffect = null)
{
var script = new Autotomize();
var move = Substitute.For();
var eventHook = new EventHook();
var battle = Substitute.For();
battle.EventHook.Returns(eventHook);
var battleData = Substitute.For();
battleData.Battle.Returns(battle);
var userVolatile = Substitute.For();
userVolatile.Get().Returns(existingEffect);
var user = Substitute.For();
user.BattleData.Returns(battleData);
user.Volatile.Returns(userVolatile);
user.WeightInKg.Returns(weightInKg);
user.ChangeStatBoost(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any())
.Returns(speedRaiseSucceeds);
move.User.Returns(user);
return (script, move, user, userVolatile, eventHook);
}
private static bool ReceivedStackOrAdd(IScriptSet scriptSet) =>
scriptSet.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "StackOrAdd");
///
/// Bulbapedia: "Autotomize raises the user's Speed stat by two stages". The stat change is self-inflicted.
///
[Test]
public void OnSecondaryEffect_RaisesUserSpeedByTwoStages()
{
// Arrange
var (script, move, user, _, _) = CreateTestSetup(100f, true);
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
}
///
/// Bulbapedia: "(if the user successfully changes its Speed) decreases its weight by 220 lbs. (100 kg).
/// If the user successfully changes its weight, the message '<Pokémon> became nimble!' is displayed."
///
[Test]
public async Task OnSecondaryEffect_SpeedRaiseSucceeds_AddsWeightReductionAndShowsMessage()
{
// Arrange
var (script, move, _, userVolatile, eventHook) = CreateTestSetup(100f, true);
DialogEvent? capturedEvent = null;
eventHook.Handler += (_, args) =>
{
if (args is DialogEvent dialogEvent)
capturedEvent = dialogEvent;
};
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
await Assert.That(capturedEvent).IsNotNull();
await Assert.That(capturedEvent!.Message).IsEqualTo("pokemon_became_nimble");
}
///
/// Bulbapedia: the weight decrease only happens "if the user successfully changes its Speed". When the
/// Speed stat cannot be raised (already at +6), no weight reduction is applied and no message is shown.
///
[Test]
public async Task OnSecondaryEffect_SpeedRaiseFails_NoWeightReductionOrMessage()
{
// Arrange
var (script, move, _, userVolatile, eventHook) = CreateTestSetup(100f, false);
var eventFired = false;
eventHook.Handler += (_, _) => eventFired = true;
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsFalse();
await Assert.That(eventFired).IsFalse();
}
///
/// Bulbapedia: "Autotomize cannot decrease the user's weight below the minimum 0.2 lbs (0.1 kg); if the
/// user's weight would drop below the minimum, it becomes the minimum instead." A first use on a Pokémon
/// lighter than 100 kg still applies the weight reduction (the weight clamps to the minimum).
///
[Test]
public async Task OnSecondaryEffect_FirstUseOnLightPokemon_StillAppliesWeightReduction()
{
// Arrange - 50 kg Pokémon, no previous Autotomize use
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true);
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
}
///
/// Bulbapedia: "Weight loss from Autotomize stacks, so using it multiple times will continue to decrease
/// the user's weight accordingly until it reaches the minimum weight" and "if the user's weight would drop
/// below the minimum, it becomes the minimum instead." A Pokémon that still weighs more than the minimum
/// after two stacks must receive another stack (its weight drops to the 0.1 kg minimum).
///
[Test]
public async Task OnSecondaryEffect_WeightWouldDropBelowMinimum_StillReducesToMinimum()
{
// Arrange - the user already has two stacks and currently weighs 50 kg
var existingEffect = new AutotomizeEffect();
existingEffect.Stack();
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true, existingEffect);
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert - the weight should still be reduced (to the minimum)
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
}
///
/// Bulbapedia: "Autotomize cannot decrease the user's weight below the minimum 0.2 lbs (0.1 kg)".
/// A Pokémon already at the minimum weight cannot lose more weight, so no new stack is added and no
/// message is shown; the Speed boost itself is still applied.
///
[Test]
public async Task OnSecondaryEffect_AlreadyAtMinimumWeight_NoFurtherReductionButSpeedStillRaised()
{
// Arrange - the user already has a stack and is at the minimum weight
var (script, move, user, userVolatile, eventHook) = CreateTestSetup(0.1f, true, new AutotomizeEffect());
var eventFired = false;
eventHook.Handler += (_, _) => eventFired = true;
// Act
script.OnSecondaryEffect(move, Substitute.For(), 0);
// Assert
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsFalse();
await Assert.That(eventFired).IsFalse();
}
}