using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Dynamic.ScriptHandling.Registry; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Plugin.Gen7.Scripts.Side; using PkmnLib.Static; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script and the side script it places. /// Gen VII Bulbapedia behavior (Generation III onwards): "Mist now creates a Mist effect on the user's side /// of the field that lasts for five turns. All Pokémon on the user's side of the field are protected while /// Mist is active, including Pokémon switched in after it was created." "While Mist is in effect, other /// Pokémon (including allies) cannot lower the stats of Pokémon protected by Mist using either moves or /// Abilities (e.g. Intimidate)", though it does not prevent self-inflicted stat reductions. /// public class MistTests { /// /// Creates a fully mocked test setup where the user's battle side has a real as /// its volatile scripts, so the Mist side effect can actually be attached and inspected. /// private static (Mist script, IExecutingMove move, IBattleSide side, IScriptSet sideScripts) CreateTestSetup() { var script = new Mist(); var move = Substitute.For(); var side = Substitute.For(); side.GetScripts().Returns(_ => new ScriptIterator(Array.Empty>())); IScriptSet sideScripts = new ScriptSet(side); side.VolatileScripts.Returns(sideScripts); var battleData = Substitute.For(); battleData.BattleSide.Returns(side); var user = Substitute.For(); user.BattleData.Returns(battleData); move.User.Returns(user); return (script, move, side, sideScripts); } /// /// Bulbapedia: "Mist now creates a Mist effect on the user's side of the field that lasts for five /// turns." — using the move attaches the side script to the user's side. /// [Test] public async Task OnSecondaryEffect_UserInBattle_AddsMistEffectToUsersSide() { // Arrange var (script, move, _, sideScripts) = CreateTestSetup(); // Act script.OnSecondaryEffect(move, Substitute.For(), 0); // Assert await Assert.That(sideScripts.Contains(ScriptUtils.ResolveName())).IsTrue(); } /// /// Technical test: without battle data on the user (outside of battle) the effect does nothing and does /// not throw. /// [Test] public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing() { // Arrange var (script, move, _, sideScripts) = CreateTestSetup(); move.User.BattleData.Returns((IPokemonBattleData?)null); // Act script.OnSecondaryEffect(move, Substitute.For(), 0); // Assert await Assert.That(sideScripts.Count).IsEqualTo(0); } /// /// Bulbapedia: "While Mist is in effect, other Pokémon (including allies) cannot lower the stats of /// Pokémon protected by Mist using either moves or Abilities (e.g. Intimidate)." — a stat reduction /// inflicted by another Pokémon is prevented by the side script. /// [Test, Arguments(Statistic.Attack, (sbyte)-1), Arguments(Statistic.Defense, (sbyte)-2), Arguments(Statistic.Speed, (sbyte)-6), Arguments(Statistic.Accuracy, (sbyte)-1)] public async Task PreventStatBoostChange_StatLoweredByOpponent_Prevented(Statistic stat, sbyte amount) { // Arrange var effect = new MistEffect(); var target = Substitute.For(); var prevent = false; // Act effect.PreventStatBoostChange(target, stat, amount, false, ref prevent); // Assert await Assert.That(prevent).IsTrue(); } /// /// Bulbapedia: Mist only stops other Pokémon from being able to "lower the stats of Pokémon protected by /// Mist" — stat increases are not affected by Mist. /// [Test, Arguments((sbyte)1), Arguments((sbyte)2), Arguments((sbyte)6)] public async Task PreventStatBoostChange_StatIncrease_NotPrevented(sbyte amount) { // Arrange var effect = new MistEffect(); var target = Substitute.For(); var prevent = false; // Act effect.PreventStatBoostChange(target, Statistic.Attack, amount, false, ref prevent); // Assert await Assert.That(prevent).IsFalse(); } /// /// Technical test: a stat change of zero stages is not a stat reduction, so Mist does not prevent it. /// [Test] public async Task PreventStatBoostChange_ZeroChange_NotPrevented() { // Arrange var effect = new MistEffect(); var target = Substitute.For(); var prevent = false; // Act effect.PreventStatBoostChange(target, Statistic.Attack, 0, false, ref prevent); // Assert await Assert.That(prevent).IsFalse(); } /// /// Bulbapedia: "While Mist is in effect, other Pokémon (including allies) cannot lower the stats of /// Pokémon protected by Mist" — Mist does not prevent self-inflicted stat reductions (such as the /// Defense/Special Defense drops from the protected Pokémon's own Superpower or Close Combat). /// [Test] public async Task PreventStatBoostChange_SelfInflictedReduction_NotPrevented() { // Arrange var effect = new MistEffect(); var target = Substitute.For(); var prevent = false; // Act effect.PreventStatBoostChange(target, Statistic.Defense, -1, true, ref prevent); // Assert await Assert.That(prevent).IsFalse(); } /// /// Bulbapedia: "Mist now creates a Mist effect on the user's side of the field that lasts for five /// turns." — after five end-of-turn ticks the removes itself from the side. /// [Test] public async Task MistEffect_AfterFiveEndOfTurns_EffectExpires() { // Arrange var (script, move, side, sideScripts) = CreateTestSetup(); script.OnSecondaryEffect(move, Substitute.For(), 0); var effect = sideScripts.Get(); await Assert.That(effect).IsNotNull(); // Act - five turns pass var battle = Substitute.For(); if (effect is IScriptOnEndTurn endTurn) { for (var i = 0; i < 5; i++) endTurn.OnEndTurn(side, battle); } // Assert await Assert.That(sideScripts.Contains(ScriptUtils.ResolveName())).IsFalse(); } }