From bf8c8c4e99b1488f5b9c68dc1ed4aa06a09aadd6 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Sat, 15 Mar 2025 17:36:46 -0400 Subject: [PATCH] fix #13425 (ninjutsu ability), add test --- .../abilities/keywords/NinjutsuTest.java | 55 +++++++++++++++++++ .../abilities/keyword/NinjutsuAbility.java | 18 ++++-- 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/NinjutsuTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/NinjutsuTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/NinjutsuTest.java new file mode 100644 index 00000000000..3b844e5a1bb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/NinjutsuTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + 702.49. Ninjutsu + 702.49a. Ninjutsu is an activated ability that functions only while the card with ninjutsu is in a player's hand. + "Ninjutsu [cost]" means "[Cost], Reveal this card from your hand, Return an unblocked attacking creature you control + to its owner's hand: Put this card onto the battlefield from your hand tapped and attacking." + 702.49b. The card with ninjutsu remains revealed from the time the ability is announced until the ability leaves the stack. + 702.49c. A ninjutsu ability may be activated only while a creature on the battlefield is unblocked (see rule 509.1h). + The creature with ninjutsu is put onto the battlefield unblocked. It will be attacking the same player, planeswalker, + or battle as the creature that was returned to its owner's hand. + 702.49d. Commander ninjutsu is a variant of the ninjutsu ability that also functions while the card with commander + ninjutsu is in the command zone. "Commander ninjutsu [cost]" means "[Cost], Reveal this card from your hand or + from the command zone, Return an unblocked attacking creature you control to its owner's hand: Put this card onto + the battlefield tapped and attacking." + */ +public class NinjutsuTest extends CardTestPlayerBase { + + private static final String drake = "Seacoast Drake"; // 1/3 flying + private static final String crab = "Jwari Scuttler"; // 2/3 + private static final String shinobi = "Moonblade Shinobi"; // 3/2, Ninjutsu 2U, on combat damage to player create 1/1 Illusion + + @Test + public void testMultipleUsage() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.BATTLEFIELD, playerA, drake); + addCard(Zone.BATTLEFIELD, playerA, crab); + addCard(Zone.HAND, playerA, shinobi); + + attack(1, playerA, drake, playerB); + attack(1, playerA, crab, playerB); + + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ninjutsu"); + setChoice(playerA, drake); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ninjutsu"); // while above ability on stack + setChoice(playerA, crab); + // result, both drake and crab in hand, shinobi on battlefield + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertPowerToughness(playerA, shinobi, 3, 2); + assertPermanentCount(playerA, "Illusion Token", 1); + assertHandCount(playerA, drake, 1); + assertHandCount(playerA, crab, 1); + assertTappedCount("Island", true, 6); // activated twice + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java index a897d732f4f..32422330d34 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java @@ -54,7 +54,7 @@ public class NinjutsuAbility extends ActivatedAbilityImpl { } public NinjutsuAbility(Cost cost, boolean commander) { - super(commander ? Zone.ALL : Zone.HAND, new NinjutsuEffect(), cost); + super(commander ? Zone.ALL : Zone.HAND, new NinjutsuEffect(commander), cost); this.addCost(new RevealNinjutsuCardCost(commander)); this.addCost(new ReturnAttackerToHandTargetCost()); this.commander = commander; @@ -84,14 +84,18 @@ public class NinjutsuAbility extends ActivatedAbilityImpl { class NinjutsuEffect extends OneShotEffect { - public NinjutsuEffect() { + private final boolean commander; + + NinjutsuEffect(boolean commander) { super(Outcome.PutCreatureInPlay); + this.commander = commander; this.staticText = "Put this card onto the battlefield " + "from your hand tapped and attacking"; } - protected NinjutsuEffect(final NinjutsuEffect effect) { + private NinjutsuEffect(final NinjutsuEffect effect) { super(effect); + this.commander = effect.commander; } @Override @@ -102,11 +106,12 @@ class NinjutsuEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + Card card = game.getCard(source.getSourceId()); + if (controller == null || card == null) { return false; } - Card card = game.getCard(source.getSourceId()); - if (card != null) { + Zone cardZone = game.getState().getZone(card.getId()); + if (cardZone == Zone.HAND || (commander && cardZone == Zone.COMMAND)) { controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { @@ -144,6 +149,7 @@ class ReturnAttackerToHandTargetCost extends CostImpl { public ReturnAttackerToHandTargetCost(ReturnAttackerToHandTargetCost cost) { super(cost); + this.defendingPlayerId = cost.defendingPlayerId; } @Override