From fc213658476161e10d5f8748d7508e3f5e5af7bf Mon Sep 17 00:00:00 2001 From: xenohedron Date: Tue, 11 Jun 2024 00:58:12 -0400 Subject: [PATCH] Fix "becomes the target of" logic to check all modes (#12452) * check all targets from all modes for getTargetingStackObject * update logic related to a97dec3 --- .../triggers/BecomesTargetTriggerTest.java | 90 +++++++++++++++++++ .../SourceTargetsPermanentCondition.java | 4 +- Mage/src/main/java/mage/util/CardUtil.java | 6 +- 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java index 6141bbd80e7..cdaed27a013 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java @@ -1,5 +1,6 @@ package org.mage.test.cards.triggers; +import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -454,4 +455,93 @@ public class BecomesTargetTriggerTest extends CardTestPlayerBase { assertPowerToughness(playerA, mariner, 3, 3); } + // Reported bugs with modal spells: #11539, #12439 + + private static final String hostility = "Borrowed Hostility"; // R Instant + // Escalate {3} (Pay this cost for each mode chosen beyond the first.) + // Choose one or both — + // • Target creature gets +3/+0 until end of turn. + // • Target creature gains first strike until end of turn. + + private static final String protector = "Angelic Protector"; // 2/2 Flying + // Whenever Angelic Protector becomes the target of a spell or ability, Angelic Protector gets +0/+3 until end of turn. + + private static final String dragon = "Goldspan Dragon"; // 4/4 Flying, haste + // Whenever Goldspan Dragon attacks or becomes the target of a spell, create a Treasure token. + + @Test + public void testFirstMode() { + addCard(Zone.BATTLEFIELD, playerA, protector); + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, hostility); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hostility); + setModeChoice(playerA, "1"); + addTarget(playerA, protector); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, protector, 5, 5); + assertAbility(playerA, protector, FirstStrikeAbility.getInstance(), false); + assertPowerToughness(playerA, dragon, 4, 4); + assertAbility(playerA, dragon, FirstStrikeAbility.getInstance(), false); + assertPermanentCount(playerA, "Treasure Token", 0); + assertTappedCount("Mountain", true, 1); + assertTappedCount("Mountain", false, 3); + } + + @Test + public void testSecondMode() { + addCard(Zone.BATTLEFIELD, playerA, protector); + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, hostility); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hostility); + setModeChoice(playerA, "2"); + addTarget(playerA, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, protector, 2, 2); + assertAbility(playerA, protector, FirstStrikeAbility.getInstance(), false); + assertPowerToughness(playerA, dragon, 4, 4); + assertAbility(playerA, dragon, FirstStrikeAbility.getInstance(), true); + assertPermanentCount(playerA, "Treasure Token", 1); + assertTappedCount("Mountain", true, 1); + assertTappedCount("Mountain", false, 3); + } + + @Test + public void testBothModes() { + addCard(Zone.BATTLEFIELD, playerA, protector); + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, hostility); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hostility); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + addTarget(playerA, protector); // +3/+0 + addTarget(playerA, dragon); // first strike + setChoice(playerA, "Whenever {this} attacks"); // order triggers + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, protector, 5, 5); + assertAbility(playerA, protector, FirstStrikeAbility.getInstance(), false); + assertPowerToughness(playerA, dragon, 4, 4); + assertAbility(playerA, dragon, FirstStrikeAbility.getInstance(), true); + assertPermanentCount(playerA, "Treasure Token", 1); + assertTappedCount("Mountain", true, 4); + assertTappedCount("Mountain", false, 0); + } + } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java index fc52b9f1282..f961c55855f 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java @@ -5,6 +5,7 @@ import mage.abilities.condition.Condition; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.stack.StackObject; +import mage.util.CardUtil; import java.util.Objects; @@ -25,9 +26,8 @@ public class SourceTargetsPermanentCondition implements Condition { if (stackObject == null) { return false; } - return stackObject.getStackAbility().getTargets() + return CardUtil.getAllSelectedTargets(stackObject.getStackAbility(), game) .stream() - .flatMap(t -> t.getTargets().stream()) .map(game::getPermanentOrLKIBattlefield) .filter(Objects::nonNull) .anyMatch(p -> filter.match(p, source.getControllerId(), source, game)); diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 8068522bea4..8815a5c831f 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1054,10 +1054,8 @@ public final class CardUtil { if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId())) { continue; } - for (Target target : stackAbility.getTargets()) { - if (target.getTargets().contains(event.getTargetId())) { - return stackObject; - } + if (CardUtil.getAllSelectedTargets(stackAbility, game).contains(event.getTargetId())) { + return stackObject; } } return null;