using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Static; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script. /// Behavior is verified against the Bulbapedia page for Knock Off (Generation VII). /// public class KnockOffTests { /// /// Creates a fully mocked test setup for Knock Off tests. When is true, /// succeeds and yields a mocked item. /// private static (KnockOff script, IExecutingMove move, IPokemon target, IPokemon user, IHitData hitData) CreateTestSetup(bool targetHasItem) { var script = new KnockOff(); var move = Substitute.For(); var target = Substitute.For(); var hitData = Substitute.For(); move.GetHitData(target, 0).Returns(hitData); target.TryStealHeldItem(out Arg.Any()).Returns(callInfo => { callInfo[0] = targetHasItem ? Substitute.For() : null; return targetHasItem; }); var user = Substitute.For(); move.User.Returns(user); return (script, move, target, user, hitData); } /// /// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one." /// (Generation V onwards: "Knock Off now removes the held item rather than rendering it unusable".) /// [Test] public async Task OnSecondaryEffect_TargetHoldsItem_ItemIsRemoved() { // Arrange var (script, move, target, _, hitData) = CreateTestSetup(true); // Act script.OnSecondaryEffect(move, target, 0); // Assert target.Received(1).TryStealHeldItem(out Arg.Any()); hitData.DidNotReceive().Fail(); await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsTrue(); } /// /// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one." /// Against a target without a held item the move simply deals its damage without any further effect; it /// does not fail. /// [Test] public async Task OnSecondaryEffect_TargetHoldsNoItem_HitIsNotMarkedAsFailed() { // Arrange var (script, move, target, _, hitData) = CreateTestSetup(false); // Act script.OnSecondaryEffect(move, target, 0); // Assert hitData.DidNotReceive().Fail(); await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse(); } /// /// Bulbapedia (Generation V onwards): "If the user faints due to the target's Ability (Rough Skin or /// Iron Barbs) or held Rocky Helmet, it cannot remove the target's held item." /// Those effects damage the user before the secondary effect runs, so a user that has fainted by then must /// not remove the item. /// [Test] public async Task OnSecondaryEffect_UserIsFainted_ItemIsNotRemoved() { // Arrange var (script, move, target, user, _) = CreateTestSetup(true); user.IsFainted.Returns(true); // Act script.OnSecondaryEffect(move, target, 0); // Assert await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsFalse(); } /// /// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can /// be knocked off, its base power will be boosted by 50%." /// With Knock Off's base power of 65 the boosted power is 97 (65 × 1.5, fractions dropped). /// [Test] public async Task ChangeBasePower_TargetHoldsRemovableItem_BasePowerBoostedByFiftyPercent() { // Arrange var (script, move, target, _, _) = CreateTestSetup(true); target.HeldItem.Returns(Substitute.For()); ushort basePower = 65; // Act ((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower); // Assert await Assert.That(basePower).IsEqualTo((ushort)97); } /// /// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can /// be knocked off, its base power will be boosted by 50%." /// A target without a held item does not grant the boost, so the base power stays unchanged. /// [Test] public async Task ChangeBasePower_TargetHoldsNoItem_BasePowerUnchanged() { // Arrange var (script, move, target, _, _) = CreateTestSetup(false); target.HeldItem.Returns((IItem?)null); ushort basePower = 65; // Act ((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower); // Assert await Assert.That(basePower).IsEqualTo((ushort)65); } }