using PkmnLib.Dynamic.Events; using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Dynamic.ScriptHandling.Registry; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Plugin.Gen7.Scripts.Pokemon; using PkmnLib.Static.Moves; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script and its . /// Gen VII Bulbapedia behavior: the user digs underground on the first turn, becoming semi-invulnerable, /// and attacks on the second turn. "Earthquake, Magnitude, and Fissure hit underground targets (dealing /// double damage from Gen II onward)". /// public class DigTests { private static (Dig script, IExecutingMove move, IPokemon user, ScriptSet userVolatile, IMoveChoice moveChoice) CreateTestSetup() { var script = new Dig(); var move = Substitute.For(); var user = Substitute.For(); // Use a real script set so the charge volatile added by Dig can be inspected afterwards. var userVolatile = new ScriptSet(user); user.Volatile.Returns(userVolatile); user.GetScripts().Returns(_ => new ScriptIterator(new List>())); move.User.Returns(user); var moveChoice = Substitute.For(); move.MoveChoice.Returns(moveChoice); var battle = Substitute.For(); battle.EventHook.Returns(new EventHook()); var battleData = Substitute.For(); battleData.Battle.Returns(battle); user.BattleData.Returns(battleData); return (script, move, user, userVolatile, moveChoice); } /// /// Bulbapedia: "The user excavates underground, becoming immune to most moves during the initial turn". /// On the first turn the move is prevented from executing. /// [Test] public async Task PreventMove_FirstUse_PreventsMoveForChargeTurn() { // Arrange var (script, move, _, _, _) = CreateTestSetup(); var prevent = false; // Act script.PreventMove(move, ref prevent); // Assert await Assert.That(prevent).IsTrue(); } /// /// Bulbapedia: the user gains "a semi-invulnerable state" on the initial turn. The charge turn /// attaches the volatile script to the user. /// [Test] public async Task PreventMove_FirstUse_AddsDigEffectToUser() { // Arrange var (script, move, _, userVolatile, _) = CreateTestSetup(); var prevent = false; // Act script.PreventMove(move, ref prevent); // Assert await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsTrue(); } /// /// The charge turn is marked on the so the knows /// this choice was the charging turn and does not remove itself for it. /// [Test] public async Task PreventMove_FirstUse_MarksMoveChoiceAsChargeTurn() { // Arrange var (script, move, _, _, moveChoice) = CreateTestSetup(); var prevent = false; // Act script.PreventMove(move, ref prevent); // Assert moveChoice.Received(1).SetAdditionalData(new StringKey("dig_charge"), true); await Assert.That(prevent).IsTrue(); } /// /// Bulbapedia: "On the following turn, Dig deals damage." When the user already has the /// from the charge turn, the move is not prevented again. /// [Test] public async Task PreventMove_SecondTurn_DoesNotPreventMove() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); userVolatile.Add(new DigEffect(user)); var prevent = false; // Act script.PreventMove(move, ref prevent); // Assert await Assert.That(prevent).IsFalse(); } /// /// Bulbapedia: "On the following turn, Dig deals damage." Once the attack executes, the underground /// state () is removed from the user. /// [Test] public async Task OnBeforeMove_ChargeCompleted_RemovesDigEffect() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); userVolatile.Add(new DigEffect(user)); // Act script.OnBeforeMove(move); // Assert await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsFalse(); } /// /// Bulbapedia: while underground the user is in "a semi-invulnerable state" and is "immune to most /// moves". The blocks incoming hits from moves that cannot hit underground /// Pokémon. /// [Test] public async Task BlockIncomingHit_MoveCannotHitUnderground_BlocksHit() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); var prevent = false; script.PreventMove(move, ref prevent); await Assert.That(userVolatile.TryGet(out var effect)).IsTrue(); var incomingMove = Substitute.For(); var incomingMoveData = Substitute.For(); incomingMoveData.HasFlag(new StringKey("hit_underground")).Returns(false); incomingMove.UseMove.Returns(incomingMoveData); // Act var block = false; effect!.BlockIncomingHit(incomingMove, user, 0, ref block); // Assert await Assert.That(block).IsTrue(); } /// /// Bulbapedia: "Earthquake, Magnitude, and Fissure hit underground targets". Moves flagged as able to /// hit underground Pokémon are not blocked by the . /// [Test] public async Task BlockIncomingHit_EarthquakeLikeMove_DoesNotBlockHit() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); var prevent = false; script.PreventMove(move, ref prevent); await Assert.That(userVolatile.TryGet(out var effect)).IsTrue(); var incomingMove = Substitute.For(); var incomingMoveData = Substitute.For(); incomingMoveData.HasFlag(new StringKey("hit_underground")).Returns(true); incomingMove.UseMove.Returns(incomingMoveData); // Act var block = false; effect!.BlockIncomingHit(incomingMove, user, 0, ref block); // Assert await Assert.That(block).IsFalse(); } /// /// Bulbapedia: "Earthquake, Magnitude, and Fissure hit underground targets (dealing double damage from /// Gen II onward)". A move flagged as effective against underground Pokémon deals doubled damage to the /// underground user. /// [Test] public async Task ChangeIncomingMoveDamage_EarthquakeLikeMove_DamageIsDoubled() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); var prevent = false; script.PreventMove(move, ref prevent); await Assert.That(userVolatile.TryGet(out var effect)).IsTrue(); var incomingMove = Substitute.For(); var incomingMoveData = Substitute.For(); incomingMoveData.HasFlag(new StringKey("hit_underground")).Returns(true); incomingMoveData.HasFlag(new StringKey("effective_against_underground")).Returns(true); incomingMove.UseMove.Returns(incomingMoveData); uint damage = 100; // Act effect!.ChangeIncomingMoveDamage(incomingMove, user, 0, ref damage); // Assert await Assert.That(damage).IsEqualTo(200u); } /// /// Bulbapedia: only Earthquake and Magnitude deal double damage to underground targets. A move without /// the effective-against-underground flag deals regular damage. /// [Test] public async Task ChangeIncomingMoveDamage_RegularMove_DamageIsUnchanged() { // Arrange var (script, move, user, userVolatile, _) = CreateTestSetup(); var prevent = false; script.PreventMove(move, ref prevent); await Assert.That(userVolatile.TryGet(out var effect)).IsTrue(); var incomingMove = Substitute.For(); var incomingMoveData = Substitute.For(); incomingMoveData.HasFlag(new StringKey("effective_against_underground")).Returns(false); incomingMove.UseMove.Returns(incomingMoveData); uint damage = 100; // Act effect!.ChangeIncomingMoveDamage(incomingMove, user, 0, ref damage); // Assert await Assert.That(damage).IsEqualTo(100u); } /// /// Bulbapedia: "If disrupted, PP isn't consumed and the move doesn't count as the last used" — when /// the user's executed choice was not the Dig charge (i.e. the move was disrupted or replaced), the /// underground state is removed. /// [Test] public async Task OnAfterMoveChoice_ChoiceIsNotDigCharge_RemovesDigEffect() { // Arrange var user = Substitute.For(); var userVolatile = new ScriptSet(user); user.GetScripts().Returns(_ => new ScriptIterator(new List>())); var effect = new DigEffect(user); userVolatile.Add(effect); var choice = Substitute.For(); choice.AdditionalData.Returns((Dictionary?)null); // Act effect.OnAfterMoveChoice(choice); // Assert await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsFalse(); } /// /// The charging turn itself carries the "dig_charge" marker; the must survive /// that turn so the user stays underground until the attack turn. /// [Test] public async Task OnAfterMoveChoice_ChoiceIsDigCharge_KeepsDigEffect() { // Arrange var user = Substitute.For(); var userVolatile = new ScriptSet(user); user.GetScripts().Returns(_ => new ScriptIterator(new List>())); var effect = new DigEffect(user); userVolatile.Add(effect); var choice = Substitute.For(); choice.AdditionalData.Returns(new Dictionary { { "dig_charge", true } }); // Act effect.OnAfterMoveChoice(choice); // Assert await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsTrue(); } }