From 9ffca1a70ba512edf9cbfa0ae0825634d8f42d12 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Tue, 19 Sep 2023 18:49:49 -0400 Subject: [PATCH 01/18] add a bunch of test cases --- .../single/c17/ScalelordReckonerTest.java | 39 ++ .../cards/single/vow/BrineComberTest.java | 27 +- .../triggers/BecomesTargetTriggerTest.java | 457 ++++++++++++++++++ .../cards/triggers/TargetedTriggeredTest.java | 183 ------- .../watchers/KiraGreatGlassSpinnerTest.java | 116 ++++- 5 files changed, 631 insertions(+), 191 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java delete mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java new file mode 100644 index 00000000000..36182bd1c81 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.single.c17; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class ScalelordReckonerTest extends CardTestPlayerBase { + + private static final String scalelord = "Scalelord Reckoner"; // 4/4 Flying + // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, + // destroy target nonland permanent that player controls. + + @Test + public void testScalelordReckoner() { + + String machete = "Trusty Machete"; // a nonland permanent to destroy + String shock = "Shock"; // a spell to target with + + addCard(Zone.BATTLEFIELD, playerA, scalelord); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, machete, 1); + addCard(Zone.HAND, playerB, shock, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, shock, scalelord); + addTarget(playerA, machete); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertDamageReceived(playerA, scalelord, 2); + assertGraveyardCount(playerB, machete, 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java index a15f46b2c07..5dd058345d9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java @@ -14,10 +14,11 @@ public class BrineComberTest extends CardTestPlayerBase { * Brine Comber * {1}{W}{U} * Creature — Spirit - * * Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - * * Disturb {W}{U} (You may cast this card from your graveyard transformed for its disturb cost.) + * Brinebound Gift + * Enchant creature + * Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. */ private static final String comber = "Brine Comber"; @@ -74,4 +75,26 @@ public class BrineComberTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Hopeful Eidolon", 1); assertPowerToughness(playerA, comber, 1+1, 1+1); } + + @Test + public void testDisturbSideTrigger() { + setStrictChooseMode(true); + + String hatchling = "Kraken Hatchling"; // 0/4 + String umbra = "Hyena Umbra"; // Aura - gives +1/+1 and first strike + addCard(Zone.BATTLEFIELD, playerA, hatchling); + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 3); + addCard(Zone.GRAVEYARD, playerA, comber); + addCard(Zone.HAND, playerA, umbra); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Brinebound Gift using Disturb", hatchling); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, umbra, hatchling); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Spirit Token", 2); // one from enter, one from target of next Aura + assertPermanentCount(playerA, "Brinebound Gift", 1); + assertPowerToughness(playerA, hatchling, 1, 5); + } } 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 new file mode 100644 index 00000000000..6141bbd80e7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java @@ -0,0 +1,457 @@ +package org.mage.test.cards.triggers; + +import mage.abilities.keyword.FlyingAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BecomesTargetTriggerTest extends CardTestPlayerBase { + + @Test + public void testAshenmoorLiege() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Claustrophobia"); // {1}{U}{U} + + // Other black creatures you control get +1/+1. + // Other red creatures you control get +1/+1. + // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. + addCard(Zone.BATTLEFIELD, playerB, "Ashenmoor Liege", 1); // 4/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Claustrophobia", "Ashenmoor Liege"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 16); + assertPermanentCount(playerA, "Claustrophobia", 1); + assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1); + } + + @Test + public void testVeneratedRotpriest() { + String rotpriest = "Venerated Rotpriest"; // 1/2 + // Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter. + String growth = "Giant Growth"; + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, rotpriest); + addCard(Zone.HAND, playerA, growth); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, growth, rotpriest); + addTarget(playerA, playerB); // to get a poison counter + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertCounterCount(playerB, CounterType.POISON, 1); + assertPowerToughness(playerA, rotpriest, 4, 5); + } + + private static final String gKeeper = "Glyph Keeper"; // 5/3 Flying + // Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability. + private static final String bolt = "Lightning Bolt"; + + @Test + public void testGlyphKeeperCountersFirstSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, bolt); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 1); + assertPermanentCount(playerB, gKeeper, 1); + } + + @Test + public void testGlyphKeeperCountersFirstSpellEachTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, bolt, 2); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 2); + assertPermanentCount(playerB, gKeeper, 1); + } + + @Test + public void testGlyphKeeperCountersFirstSpellButNotSecondSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, bolt, 2); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.DECLARE_ATTACKERS); + execute(); + + assertGraveyardCount(playerA, bolt, 2); + assertPermanentCount(playerB, gKeeper, 0); + } + + @Test + public void testGlyphKeeperCountersFirstAbilityButNotSecondOne() { + /* + Soulstinger {3}{B} + Creature - Scorpion Demon 4/5 + When Soulstinger enters the battlefield, put two -1/-1 counter on target creature you control. + When Soulstinger dies, you may put a -1/-1 counter on target creature for each -1/-1 counter on Soulstinger. + */ + String sStinger = "Soulstinger"; + + /* + Cartouche of Strength {2}{G} + Enchantment - Aura Cartouche + Enchant creature you control + When Cartouche of Strength enters the battlefield, you may have enchanted creature fight target creature an opponent controls. + Enchanted creature gets +1/+1 and has trample. + */ + String cStrength = "Cartouche of Strength"; + String memnite = "Memnite"; // {0} 1/1 + + addCard(Zone.BATTLEFIELD, playerA, gKeeper); + addCard(Zone.HAND, playerA, sStinger); + addCard(Zone.HAND, playerA, cStrength); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.BATTLEFIELD, playerB, memnite); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sStinger); + addTarget(playerA, gKeeper); // should be countered by Glyph Keeper clause as first ability targetting it + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cStrength, gKeeper); // should not be countered anymore + setChoice(playerA, true); + addTarget(playerA, memnite); // Cartouche of Strength fight + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, gKeeper, 1); + assertGraveyardCount(playerA, sStinger, 0); // countered + assertGraveyardCount(playerA, cStrength, 0); // should not be countered + assertPermanentCount(playerA, cStrength, 1); + assertGraveyardCount(playerB, memnite, 1); // dies from fight + assertPowerToughness(playerA, gKeeper, 6, 4, Filter.ComparisonScope.All); + // Soul Stinger should never have given it two -1/-1 counters + // And Cartouche of Strength gives +1/+1 + assertCounterCount(playerA, gKeeper, CounterType.M1M1, 0); + } + + @Test + public void testDiffusionSliver() { + String diffusion = "Diffusion Sliver"; // 1/1 Sliver + // Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {2}. + String metallic = "Metallic Sliver"; // 1/1 Sliver + + addCard(Zone.BATTLEFIELD, playerA, diffusion); + addCard(Zone.BATTLEFIELD, playerA, metallic); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt, 1); + addCard(Zone.BATTLEFIELD, playerB, "Cunning Sparkmage", 1); // 0/1 Haste + // {T}: Cunning Sparkmage deals 1 damage to any target. + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, metallic); + setChoice(playerB, false); // choose not to pay + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: {this} deals", diffusion); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertTapped("Cunning Sparkmage", true); + assertPermanentCount(playerA, diffusion, 1); + assertPermanentCount(playerA, metallic, 1); + } + + @Test + public void testThunderbreakRegent() { + String dragon = "Thunderbreak Regent"; // 4/4 + // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, Thunderbreak Regent deals 3 damage to that player. + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerA, dragon, 3); + assertLife(playerB, 17); + } + + @Test + public void testCloudCover() { + String cloud = "Cloud Cover"; // Enchantment + // Whenever another permanent you control becomes the target of a spell or ability an opponent controls, + // you may return that permanent to its owner’s hand. + String myr = "Omega Myr"; // 1/2 + + addCard(Zone.BATTLEFIELD, playerB, "Cunning Sparkmage"); + addCard(Zone.BATTLEFIELD, playerA, cloud); + addCard(Zone.BATTLEFIELD, playerA, myr); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: {this} deals", myr); + setChoice(playerA, true); // return to hand + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, myr, 1); + assertTapped("Cunning Sparkmage", true); + } + + @Test + public void testIllusionaryArmor() { + String armor = "Illusionary Armor"; // Enchantment - Aura {4}{U} + // Enchant creature + // Enchanted creature gets +4/+4. + // When enchanted creature becomes the target of a spell or ability, sacrifice Illusionary Armor. + String beast = "Axebane Beast"; // 3/4 + + addCard(Zone.BATTLEFIELD, playerA, beast); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.HAND, playerA, armor); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, armor, beast); + checkPT("Boosted", 1, PhaseStep.BEGIN_COMBAT, playerA, beast, 7, 8); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, beast); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, armor, 1); + assertPowerToughness(playerA, beast, 3, 4); + assertDamageReceived(playerA, beast, 3); + } + + @Test + public void testFracturedLoyalty() { + String hatchling = "Kraken Hatchling"; // 0/4 + String aura = "Fractured Loyalty"; // Enchantment - Aura {1}{R} + // Whenever enchanted creature becomes the target of a spell or ability, + // that spell or ability's controller gains control of that creature. (This effect lasts indefinitely.) + + addCard(Zone.BATTLEFIELD, playerA, hatchling); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, aura); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aura, hatchling); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, hatchling); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertPermanentCount(playerA, hatchling, 0); + assertDamageReceived(playerB, hatchling, 3); + } + + @Test + public void testDormantGomazoa() { + String gomazoa = "Dormant Gomazoa"; // 5/5 Flying {1}{U}{U} + // Dormant Gomazoa enters the battlefield tapped. + // Dormant Gomazoa doesn't untap during your untap step. + // Whenever you become the target of a spell, you may untap Dormant Gomazoa. + + addCard(Zone.HAND, playerA, gomazoa); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gomazoa); + checkPermanentTapped("Enters tapped", 1, PhaseStep.BEGIN_COMBAT, playerA, gomazoa, true, 1); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, playerA); + setChoice(playerA, true); // choose to untap + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertTapped(gomazoa, false); + assertLife(playerA, 17); + } + + private static final String mammoth = "Battle Mammoth"; // 6/5 Trample + // Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. + private static final String bear = "Runeclaw Bear"; // 2/2 + private static final String cBond = "Common Bond"; // 1GW Instant + // Put a +1/+1 counter on target creature. Put a +1/+1 counter on target creature. + + @Test + public void testBattleMammothSeparateTargets() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Savannah", 3); + addCard(Zone.HAND, playerB, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, cBond, mammoth+"^"+bear); + setChoice(playerA, "Whenever"); // order two triggers + setChoice(playerA, true); // draw a card + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, cBond, 1); + assertPowerToughness(playerA, mammoth, 7, 6); + assertPowerToughness(playerA, bear, 3, 3); + assertHandCount(playerA, 2); + } + + @Test + public void testBattleMammothSameTarget() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Savannah", 3); + addCard(Zone.HAND, playerB, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, cBond, bear+"^"+bear); + // Should be only one trigger here + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, cBond, 1); + assertPowerToughness(playerA, bear, 4, 4); + assertHandCount(playerA, 1); + } + + @Test + public void testBattleMammothRepeatAbility() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 8); + addCard(Zone.BATTLEFIELD, playerB, "Shapers of Nature"); // 3/3 + // {3}{G}: Put a +1/+1 counter on target creature. + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{3}{G}: Put", bear); + setChoice(playerA, true); // draw a card + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{3}{G}: Put", bear); + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, bear, 4, 4); + assertHandCount(playerA, 2); + } + + @Test + public void testAngelicCubDoubleTarget() { + String cub = "Angelic Cub"; // 1/1 + // Whenever Angelic Cub becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it. + // As long as Angelic Cub has three or more +1/+1 counters on it, it has flying. + + addCard(Zone.BATTLEFIELD, playerA, cub); + addCard(Zone.BATTLEFIELD, playerA, "Savannah", 3); + addCard(Zone.HAND, playerA, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cBond, cub+"^"+cub); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, cub, 4, 4); + assertAbility(playerA, cub, FlyingAbility.getInstance(), true); + } + + @Test + public void testUnsettledMarinerFieldOfRuin() { + // Reported bug: #10530 + String mariner = "Unsettled Mariner"; // 2/2 + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {1}. + String ruin = "Field of Ruin"; // 2/2 + // {2}, {T}, Sacrifice Field of Ruin: Destroy target nonbasic land an opponent controls. + // Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles. + + addCard(Zone.BATTLEFIELD, playerA, mariner); + addCard(Zone.BATTLEFIELD, playerA, "Evolving Wilds"); + addCard(Zone.BATTLEFIELD, playerB, ruin); + addCard(Zone.BATTLEFIELD, playerB, "Wastes", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, {T}, Sacrifice"); + addTarget(playerB, "Evolving Wilds"); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, ruin, 1); + } + + @Test + public void testCounterAbilitySacrificedSource() { + // related to #10530, but this one works fine regardless... + String mariner = "Unsettled Mariner"; // 2/2 + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {1}. + String felidar = "Felidar Cub"; // 2/2 + // Sacrifice Felidar Cub: Destroy target enchantment. + String anthem = "Glorious Anthem"; // Creatures you control get +1/+1. + + addCard(Zone.BATTLEFIELD, playerA, mariner); + addCard(Zone.BATTLEFIELD, playerA, anthem); + addCard(Zone.BATTLEFIELD, playerB, felidar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + addTarget(playerB, anthem); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, felidar, 1); + assertPowerToughness(playerA, mariner, 3, 3); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java deleted file mode 100644 index a58df8f083f..00000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java +++ /dev/null @@ -1,183 +0,0 @@ - -package org.mage.test.cards.triggers; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.Filter; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class TargetedTriggeredTest extends CardTestPlayerBase { - - /** - * Tests that the first spell that targets Kira, Great Glass-Spinner is - * countered. - * - */ - @Test - // this does not currently work in test (????), because the target event will be fired earlier during tests, - // so the zone change counter for the fixed target of the counterspell will not work - // UPDATE: seems to work fine now? 04/19/2017 escplan9 - public void testKiraGreatGlassSpinnerFirstSpellTurn() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - addCard(Zone.HAND, playerA, "Lightning Bolt"); - - addCard(Zone.BATTLEFIELD, playerB, "Kira, Great Glass-Spinner", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Kira, Great Glass-Spinner"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Lightning Bolt", 1); - - assertPermanentCount(playerB, "Kira, Great Glass-Spinner", 1); - } - - /** - * With Ashenmoor Liege on the battlefield, my opponent casts Claustrophobia - * on it without losing 4hp. - */ - @Test - public void testAshenmoorLiege() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - addCard(Zone.HAND, playerA, "Claustrophobia"); // {1}{U}{U} - - // Other black creatures you control get +1/+1. - // Other red creatures you control get +1/+1. - // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. - addCard(Zone.BATTLEFIELD, playerB, "Ashenmoor Liege", 1); // 4/1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Claustrophobia", "Ashenmoor Liege"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertLife(playerA, 16); - - assertPermanentCount(playerA, "Claustrophobia", 1); - assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1); - } - - @Test - public void testGlyphKeeperCountersFirstSpell() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg - - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - addCard(Zone.HAND, playerA, bolt); - - addCard(Zone.BATTLEFIELD, playerB, gKeeper); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, bolt, 1); - assertPermanentCount(playerB, gKeeper, 1); - } - - @Test - public void testGlyphKeeperCountersFirstSpellButNotSecondSpell() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg - - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.HAND, playerA, bolt, 2); - - addCard(Zone.BATTLEFIELD, playerB, gKeeper); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, bolt, gKeeper); - - setStopAt(1, PhaseStep.DECLARE_ATTACKERS); - execute(); - - assertGraveyardCount(playerA, bolt, 2); - assertPermanentCount(playerB, gKeeper, 0); - } - - /* - NOTE: test was failing due to card bug, resolved as of 04/20/2017. See issue #3180 - I had a Glyph Keeper on board (cloned with Vizier of many faces). -- note this test is a simplified version, next test will test on the Clone if needed - First I played a Soulstinger and targeted the Glyph Keeper, the ability was countered. Then on the same main phase I played a Cartouche of Strength targeting the Glyph Keeper, that was also countered. - Only the first should have been countered. - */ - @Test - public void testGlyphKeeperCountersFirstAbilityButNotSecondOne() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - - /* - Soulstinger {3}{B} - Creature - Scorpion Demon 4/5 - When Soulstinger enters the battlefield, put two -1/-1 counter on target creature you control. - When Soulstinger dies, you may put a -1/-1 counter on target creature for each -1/-1 counter on Soulstinger. - */ - String sStinger = "Soulstinger"; - - /* - Cartouche of Strength {2}{G} - Enchantment - Aura Cartouche - Enchant creature you control - When Cartouche of Strength enters the battlefield, you may have enchanted creature fight target creature an opponent controls. - Enchanted creature gets +1/+1 and has trample. - */ - String cStrength = "Cartouche of Strength"; - String memnite = "Memnite"; // {0} 1/1 - - addCard(Zone.BATTLEFIELD, playerA, gKeeper); - addCard(Zone.HAND, playerA, sStinger); - addCard(Zone.HAND, playerA, cStrength); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); - addCard(Zone.BATTLEFIELD, playerB, memnite); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sStinger); - addTarget(playerA, gKeeper); // should be countered by Glyph Keeper clause as first ability targetting it - waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cStrength, gKeeper); // should not be countered anymore - setChoice(playerA, true); - addTarget(playerA, memnite); // Cartouche of Strength fight - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - setStrictChooseMode(true); - execute(); - - assertPermanentCount(playerA, gKeeper, 1); - assertGraveyardCount(playerA, sStinger, 0); // countered - assertGraveyardCount(playerA, cStrength, 0); // should not be countered - assertPermanentCount(playerA, cStrength, 1); - assertGraveyardCount(playerB, memnite, 1); // dies from fight - assertPowerToughness(playerA, gKeeper, 6, 4, Filter.ComparisonScope.All); // Soul Stinger should never have given it two -1/-1 counters - //And Cartouche of Strength gives +1/+1 - assertCounterCount(playerA, gKeeper, CounterType.M1M1, 0); - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java index bb493a311b0..b07ff85a931 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java @@ -17,17 +17,19 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { Flying Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability." */ - private final String kira = "Kira, Great Glass-Spinner"; - private final String ugin = "Ugin, the Spirit Dragon"; + private static final String kira = "Kira, Great Glass-Spinner"; + private static final String ugin = "Ugin, the Spirit Dragon"; + private static final String bolt = "Lightning Bolt"; @Test public void counterFirst() { - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, kira); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage", kira); // Ugin ability + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -38,8 +40,7 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { @Test public void counterFirstResolveSecond() { - String ugin = "Ugin, the Spirit Dragon"; - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.HAND, playerA, "Unsummon", 1); @@ -48,6 +49,7 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unsummon", kira); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -59,12 +61,13 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { @Test public void counterFirstThisTurn_counterFirstOnNextTurn() { - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, kira); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage to", kira); // Ugin ability activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage to", kira); // Ugin ability + setStrictChooseMode(true); setStopAt(3, PhaseStep.END_TURN); execute(); @@ -72,4 +75,105 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { assertCounterCount(playerA, ugin, CounterType.LOYALTY, 11); } + + @Test + public void testKiraGreatGlassSpinnerFirstSpellTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, kira); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 1); + assertPermanentCount(playerB, kira, 1); + } + + @Test + public void testKiraGreatGlassSpinnerRightAbility() { + // Reported bug: #8026 + addCard(Zone.BATTLEFIELD, playerA, "Steadfast Guard", 1); // 2/2 Vigilance + addCard(Zone.BATTLEFIELD, playerA, "Angelic Benediction", 1); // Exalted + // Whenever a creature you control attacks alone, you may tap target creature. + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + attack(1, playerA, "Steadfast Guard", playerB); + setChoice(playerA, "Whenever"); // choose trigger order - exalted at top of stack + addTarget(playerA, kira); // creature to tap + //setChoice(playerA, true); // choose yes to tap - gets countered before choice needed + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 17); // exalted pumps 2/2 to 3/3, so 3 life lost + assertTapped(kira, false); // tap ability was countered + } + + @Test + public void testKiraGreatGlassSpinnerRightAbilityOtherOrder() { + // Reported bug: #8026 + addCard(Zone.BATTLEFIELD, playerA, "Steadfast Guard", 1); // 2/2 Vigilance + addCard(Zone.BATTLEFIELD, playerA, "Angelic Benediction", 1); // Exalted + // Whenever a creature you control attacks alone, you may tap target creature. + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + attack(1, playerA, "Steadfast Guard", playerB); + setChoice(playerA, "exalted"); // just in case... try the other trigger order + addTarget(playerA, kira); // creature to tap + //setChoice(playerA, true); // choose yes to tap - gets countered before choice needed + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 17); // exalted pumps 2/2 to 3/3, so 3 life lost + assertTapped(kira, false); // tap ability was countered + } + + @Test + public void counterFirstBlinkCounterNext() { + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + addCard(Zone.HAND, playerA, "Shock", 1); + addCard(Zone.HAND, playerA, "Cloudshift", 1); + addCard(Zone.BATTLEFIELD, playerA, kira); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage", kira); // Ugin ability, countered + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Cloudshift", kira); // not countered (second time being targeted) + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shock", kira); // countered (zcc has changed, so first time again) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, kira, 1); + assertCounterCount(playerA, ugin, CounterType.LOYALTY, 9); + assertGraveyardCount(playerA, "Shock", 1); + assertGraveyardCount(playerA, "Cloudshift", 1); + } + + @Test + public void counterCorrectlyMultipleOnStack() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.HAND, playerA, "Battlegrowth", 1); // Instant {G} Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Dragonscale Boon", 1); // Instant {3}{G} Put two +1/+1 counters on target creature and untap it. + addCard(Zone.BATTLEFIELD, playerA, kira); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Battlegrowth", kira); // countered + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonscale Boon", kira); // not countered (second target) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, kira, 1); + assertGraveyardCount(playerA, "Battlegrowth", 1); + assertGraveyardCount(playerA, "Dragonscale Boon", 1); + assertPowerToughness(playerA, kira, 4, 4); + } + } From 0086c5e4a209359bf49d8fdbb838004840806c35 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Wed, 20 Sep 2023 23:23:22 -0400 Subject: [PATCH 02/18] add util methods getTargetingStackObject, checkTargetMap --- Mage/src/main/java/mage/util/CardUtil.java | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 18ecb0892f0..d20a4e8c9ee 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -38,6 +38,7 @@ import mage.game.permanent.PermanentMeld; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetCard; @@ -1009,6 +1010,58 @@ public final class CardUtil { .collect(Collectors.toSet()); } + /** + * For finding the spell or ability on the stack for "becomes the target" triggers. + * @param event the GameEvent.EventType.TARGETED from checkTrigger() + * @param game the Game from checkTrigger() + * @return the StackObject which targeted the source, or null if not found + */ + public static StackObject getTargetingStackObject(GameEvent event, Game game) { + // In case of multiple simultaneous triggered abilities from the same source, + // need to get the actual one that targeted, see #8026, #8378 + // Also avoids triggering on cancelled selections, see #8802 + for (StackObject stackObject : game.getStack()) { + Ability stackAbility = stackObject.getStackAbility(); + if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId())) { + continue; + } + for (Target target : stackAbility.getTargets()) { + if (target.getTargets().contains(event.getTargetId())) { + return stackObject; + } + } + } + return null; + } + + /** + * For ensuring that spells/abilities that target the same object twice only trigger "becomes the target" abilities once + * @param triggerId this.id of the TriggeredAbility + * @param targetingObject from getTargetingStackObject + * @param event the GameEvent.EventType.TARGETED from checkTrigger() + * @param game the Game from checkTrigger() + * @return false if already triggered, true if this is the first/only trigger + */ + public static boolean checkTargetMap(UUID triggerId, StackObject targetingObject, GameEvent event, Game game) { + // If a spell or ability an opponent controls targets a single permanent you control more than once, + // Battle Mammoth's triggered ability will trigger only once. + // However, if a spell or ability an opponent controls targets multiple permanents you control, + // Battle Mammoth's triggered ability will trigger once for each of those permanents. (2021-02-05) + Map> targetMap = (Map>) game.getState().getValue("targetMap" + triggerId); + // targetMap: key - targetId; value - Set of stackObject Ids + if (targetMap == null) { + targetMap = new HashMap<>(); + } + Set targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>()); + if (!targetingObjects.add(targetingObject.getId())) { + return false; // The triggered ability already recorded that target of the stack object + } + // Otherwise, store this combination of triggered ability / target / stack object + targetMap.put(event.getTargetId(), targetingObjects); + game.getState().setValue("targetMap" + triggerId, targetMap); + return true; + } + /** * Put card to battlefield without resolve (for cheats and tests only) * From ee6872686590358c2dc1d90f3cebbf4c36fa7562 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 00:12:47 -0400 Subject: [PATCH 03/18] use common methods for WardAbility --- .../mage/abilities/keyword/WardAbility.java | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java index 03a4c667362..c80fe36ee9a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -11,7 +11,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; -import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; @@ -22,7 +21,6 @@ public class WardAbility extends TriggeredAbilityImpl { private final Cost cost; private final DynamicValue genericMana; - private final boolean showAbilityHint; private final String whereXIs; @@ -74,29 +72,19 @@ public class WardAbility extends TriggeredAbilityImpl { return event.getType() == GameEvent.EventType.TARGETED; } - private StackObject getTargetingObject(GameEvent event, Game game) { - for (StackObject stackObject : game.getStack()) { - if (stackObject.getId().equals(event.getSourceId()) || stackObject.getSourceId().equals(event.getSourceId())) { - for (Target target : stackObject.getStackAbility().getTargets()) { - if (target.contains(getSourceId())) { - return stackObject; - } - } - } - } - return null; - } - @Override public boolean checkTrigger(GameEvent event, Game game) { if (!getSourceId().equals(event.getTargetId())) { return false; } - StackObject stackObject = getTargetingObject(event, game); - if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !game.getOpponents(getControllerId()).contains(targetingObject.getControllerId())) { return false; } - getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); return true; } From 79b10856b85b7ae9ea6607b7c92971096ec0c488 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 00:23:00 -0400 Subject: [PATCH 04/18] use common methods in BecomesTargetAttachedTriggeredAbility --- .../src/mage/cards/s/SpectralPrison.java | 2 +- ...BecomesTargetAttachedTriggeredAbility.java | 31 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SpectralPrison.java b/Mage.Sets/src/mage/cards/s/SpectralPrison.java index 75d89842814..72b67c6d566 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralPrison.java +++ b/Mage.Sets/src/mage/cards/s/SpectralPrison.java @@ -37,7 +37,7 @@ public final class SpectralPrison extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); // When enchanted creature becomes the target of a spell, sacrifice Spectral Prison. - this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A)); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A, false)); } private SpectralPrison(final SpectralPrison card) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index 125dbc3c882..4b619a2ae7b 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -9,6 +9,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * @author LoneFox @@ -18,22 +19,18 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl private final FilterStackObject filter; public BecomesTargetAttachedTriggeredAbility(Effect effect) { - this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A, false); } - public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter) { - this(effect, filter, "creature"); - } - - public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, String enchantType) { - super(Zone.BATTLEFIELD, effect); - this.filter = filter.copy(); - setTriggerPhrase("When enchanted " + enchantType + " becomes the target of " + filter.getMessage() + ", "); + public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + setTriggerPhrase("When enchanted creature becomes the target of " + filter.getMessage() + ", "); } protected BecomesTargetAttachedTriggeredAbility(final BecomesTargetAttachedTriggeredAbility ability) { super(ability); - this.filter = ability.filter.copy(); + this.filter = ability.filter; } @Override @@ -49,13 +46,13 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent enchantment = game.getPermanent(sourceId); - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - if (event.getTargetId().equals(enchantment.getAttachedTo()) - && filter.match(sourceObject, getControllerId(), this, game)) { - return true; - } + if (enchantment == null || enchantment.getAttachedTo() == null || !event.getTargetId().equals(enchantment.getAttachedTo())) { + return false; } - return false; + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { + return false; + } + return CardUtil.checkTargetMap(this.id, targetingObject, event, game); } } From 65523b9fecfa63a95e9caa24b001266d780c2eba Mon Sep 17 00:00:00 2001 From: xenohedron Date: Tue, 19 Sep 2023 23:54:55 -0400 Subject: [PATCH 05/18] new common BecomesTargetControllerTriggeredAbility remove old classes --- .../src/mage/cards/a/AmuletOfSafekeeping.java | 6 +- .../src/mage/cards/d/DormantGomazoa.java | 8 +- .../mage/cards/l/LeovoldEmissaryOfTrest.java | 9 +- .../mage/cards/p/ParnesseTheSubtleBrush.java | 6 +- .../mage/cards/r/RayneAcademyChancellor.java | 7 +- .../src/mage/cards/u/UnsettledMariner.java | 14 ++-- ...TargetControllerSpellTriggeredAbility.java | 45 ---------- ...comesTargetControllerTriggeredAbility.java | 84 +++++++++++++++++++ ...ponentsSpellOrAbilityTriggeredAbility.java | 82 ------------------ 9 files changed, 112 insertions(+), 149 deletions(-) delete mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java create mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java delete mode 100644 Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java b/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java index 609f18f196a..fc149d9c60e 100644 --- a/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java +++ b/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java @@ -1,7 +1,7 @@ package mage.cards.a; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -24,7 +25,8 @@ public final class AmuletOfSafekeeping extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Whenever you become the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), false, true)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), + null, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); // Creature tokens get -1/-0. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/d/DormantGomazoa.java b/Mage.Sets/src/mage/cards/d/DormantGomazoa.java index a1e3732b1c1..f1a3d32dbcb 100644 --- a/Mage.Sets/src/mage/cards/d/DormantGomazoa.java +++ b/Mage.Sets/src/mage/cards/d/DormantGomazoa.java @@ -1,9 +1,8 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesTargetControllerSpellTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; @@ -12,8 +11,10 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; /** * @@ -37,7 +38,8 @@ public final class DormantGomazoa extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); // Whenever you become the target of a spell, you may untap Dormant Gomazoa. - this.addAbility(new BecomesTargetControllerSpellTriggeredAbility(new UntapSourceEffect(), true).setTriggerPhrase("Whenever you become the target of a spell, ")); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new UntapSourceEffect(), + null, StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, true)); } private DormantGomazoa(final DormantGomazoa card) { diff --git a/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java b/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java index a872ffee356..41a08bdb4d5 100644 --- a/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java +++ b/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java @@ -1,15 +1,15 @@ - package mage.cards.l; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -35,7 +35,8 @@ public final class LeovoldEmissaryOfTrest extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LeovoldEmissaryOfTrestEffect()), new CardsAmountDrawnThisTurnWatcher()); // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new DrawCardSourceControllerEffect(1), true, false)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private LeovoldEmissaryOfTrest(final LeovoldEmissaryOfTrest card) { @@ -81,4 +82,4 @@ class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { return watcher != null && controller != null && watcher.getAmountCardsDrawn(event.getPlayerId()) >= 1 && game.isOpponent(controller, event.getPlayerId()); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java b/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java index 6e8267d3d5e..528fc9882b0 100644 --- a/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java +++ b/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java @@ -3,13 +3,14 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -35,7 +36,8 @@ public class ParnesseTheSubtleBrush extends CardImpl { // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, // counter that spell or ability unless that player pays 4 life. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new PayLifeCost(4).setText("4 life")))); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new PayLifeCost(4).setText("4 life")), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); // Whenever you copy a spell, up to one target opponent may also copy that spell. // They may choose new targets for that copy. diff --git a/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java b/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java index a2a49b2c2e4..03ef29027f5 100644 --- a/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java +++ b/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.condition.common.EnchantedSourceCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; @@ -10,8 +10,10 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; +import mage.filter.StaticFilters; import java.util.UUID; @@ -38,7 +40,8 @@ public final class RayneAcademyChancellor extends CardImpl { new EnchantedSourceCondition(), "you may draw a card. You may draw an additional card if {this} is enchanted" ); - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(drawEffect, true, false)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(drawEffect, + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private RayneAcademyChancellor(final RayneAcademyChancellor card) { diff --git a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java index 36097f1a93d..7f945c7da06 100644 --- a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java +++ b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java @@ -1,21 +1,16 @@ package mage.cards.u; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.ChangelingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; import java.util.UUID; @@ -36,7 +31,8 @@ public final class UnsettledMariner extends CardImpl { // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, // counter that spell or ability unless its controller pays {1}. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)))); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private UnsettledMariner(final UnsettledMariner card) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java deleted file mode 100644 index c7322c34017..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java +++ /dev/null @@ -1,45 +0,0 @@ - - -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.game.events.GameEvent; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.stack.Spell; - -/** - * @author jeffwadsworth - */ -public class BecomesTargetControllerSpellTriggeredAbility extends TriggeredAbilityImpl { - - public BecomesTargetControllerSpellTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - setTriggerPhrase("When you become the target of a spell, "); - } - - protected BecomesTargetControllerSpellTriggeredAbility(final BecomesTargetControllerSpellTriggeredAbility ability) { - super(ability); - } - - @Override - public BecomesTargetControllerSpellTriggeredAbility copy() { - return new BecomesTargetControllerSpellTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(controllerId)) { - if (game.getObject(event.getSourceId()) instanceof Spell) { - return true; - } - } - return false; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java new file mode 100644 index 00000000000..b3eb9434c0a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java @@ -0,0 +1,84 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterPermanent; +import mage.filter.FilterStackObject; +import mage.game.events.GameEvent; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class BecomesTargetControllerTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filterTarget; + private final FilterStackObject filterStack; + private final SetTargetPointer setTargetPointer; + + /** + * Note: filterTarget can be null for "whenever you become the target of..."; + * if set, then "whenever you or a [filterTarget] becomes the target of..." + */ + public BecomesTargetControllerTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, + SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filterTarget = filterTarget; + this.filterStack = filterStack; + this.setTargetPointer = setTargetPointer; + String filterMessage = (filterTarget == null) + ? "you become" + : "you or " + filterTarget.getMessage() + " becomes"; + setTriggerPhrase("Whenever " + filterMessage + " the target of " + filterStack.getMessage() + ", "); + } + + protected BecomesTargetControllerTriggeredAbility(final BecomesTargetControllerTriggeredAbility ability) { + super(ability); + this.filterTarget = ability.filterTarget; + this.filterStack = ability.filterStack; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public BecomesTargetControllerTriggeredAbility copy() { + return new BecomesTargetControllerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || filterTarget == null || !filterTarget.match(permanent, getControllerId(), this, game)) { + return false; + } + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetControllerTriggeredAbility"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java deleted file mode 100644 index 29bf7131c98..00000000000 --- a/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java +++ /dev/null @@ -1,82 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; - -/** - * "Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls," - * AND - * "Whenever you become the target of a spell or ability an opponent controls," - * - * @author Alex-Vasile - */ -public class TargetOfOpponentsSpellOrAbilityTriggeredAbility extends TriggeredAbilityImpl { - - private boolean onlyController = false; - - public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect) { - this(effect, false, false); - } - - public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect, boolean optional, boolean onlyController) { - super(Zone.BATTLEFIELD, effect, optional); - this.onlyController = onlyController; - if (this.onlyController) { - setTriggerPhrase("Whenever you become the target of a spell or ability an opponent controls, "); - } else { - setTriggerPhrase("Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, "); - } - } - - private TargetOfOpponentsSpellOrAbilityTriggeredAbility(final TargetOfOpponentsSpellOrAbilityTriggeredAbility ability) { - super(ability); - this.onlyController = ability.onlyController; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player controller = game.getPlayer(this.getControllerId()); - Player targetter = game.getPlayer(event.getPlayerId()); - if (controller == null || targetter == null || controller.getId().equals(targetter.getId())) { - return false; - } - - // Check if player was targeted - if (controller.getId().equals(event.getTargetId())) { - // Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping) - this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } - - // If only the controller is - if (this.onlyController) { - return false; - } - - // Check if permanent was targeted - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !controller.getId().equals(permanent.getControllerId())) { - return false; - } - - // Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping) - this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } - - @Override - public TargetOfOpponentsSpellOrAbilityTriggeredAbility copy() { - return new TargetOfOpponentsSpellOrAbilityTriggeredAbility(this); - } -} From 6abe9ff821dda7070549bdb3e59b2c49c4d0521c Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 00:19:12 -0400 Subject: [PATCH 06/18] new common BecomesTargetAnyTriggeredAbility --- Mage.Sets/src/mage/cards/b/BattleMammoth.java | 8 +- Mage.Sets/src/mage/cards/c/CloudCover.java | 69 +++---------- Mage.Sets/src/mage/cards/c/Cowardice.java | 53 ++-------- .../src/mage/cards/d/DaruSpiritualist.java | 56 ++--------- .../src/mage/cards/d/DiffusionSliver.java | 62 ++---------- .../mage/cards/e/ElrondMasterOfHealing.java | 17 +--- Mage.Sets/src/mage/cards/f/FeySteed.java | 85 +++++----------- .../src/mage/cards/h/HorobiDeathsWail.java | 53 ++-------- Mage.Sets/src/mage/cards/j/JumpTrooper.java | 70 ++----------- .../mage/cards/k/KingOfTheOathbreakers.java | 4 +- .../src/mage/cards/m/MilaCraftyCompanion.java | 6 +- .../src/mage/cards/s/ShapersSanctuary.java | 60 ++---------- .../src/mage/cards/s/SilverfurPartisan.java | 79 ++++----------- Mage.Sets/src/mage/cards/s/SwarmShambler.java | 54 ++--------- .../src/mage/cards/t/ThunderbreakRegent.java | 73 +++----------- .../src/mage/cards/v/VeneratedRotpriest.java | 63 +++--------- Mage.Sets/src/mage/cards/w/WildDefiance.java | 68 ++----------- .../BecomesTargetAnyTriggeredAbility.java | 97 +++++++++++++++++++ ...omesTargetOpponentAllTriggeredAbility.java | 83 ---------------- .../common/BecomesTargetTriggeredAbility.java | 73 -------------- 20 files changed, 257 insertions(+), 876 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java delete mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java delete mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/b/BattleMammoth.java b/Mage.Sets/src/mage/cards/b/BattleMammoth.java index 27095dc028a..d5f86b409b6 100644 --- a/Mage.Sets/src/mage/cards/b/BattleMammoth.java +++ b/Mage.Sets/src/mage/cards/b/BattleMammoth.java @@ -1,14 +1,16 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.ForetellAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import mage.filter.StaticFilters; import java.util.UUID; @@ -28,8 +30,8 @@ public final class BattleMammoth extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), true + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); // Foretell {2}{G}{G} diff --git a/Mage.Sets/src/mage/cards/c/CloudCover.java b/Mage.Sets/src/mage/cards/c/CloudCover.java index 79d6553ad70..e6f80ec18ae 100644 --- a/Mage.Sets/src/mage/cards/c/CloudCover.java +++ b/Mage.Sets/src/mage/cards/c/CloudCover.java @@ -1,31 +1,35 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.constants.SetTargetPointer; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; /** * - * @author fireshoes + * @author xenohedron */ public final class CloudCover extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("another permanent you control"); + static { + filter.add(AnotherPredicate.instance); + } + public CloudCover(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{U}"); // Whenever another permanent you control becomes the target of a spell or ability an opponent controls, you may return that permanent to its owner's hand. - this.addAbility(new CloudCoverAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect(), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PERMANENT, true)); } private CloudCover(final CloudCover card) { @@ -37,44 +41,3 @@ public final class CloudCover extends CardImpl { return new CloudCover(this); } } - -class CloudCoverAbility extends TriggeredAbilityImpl { - - public CloudCoverAbility() { - super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), true); - } - - private CloudCoverAbility(final CloudCoverAbility ability) { - super(ability); - } - - @Override - public CloudCoverAbility copy() { - return new CloudCoverAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - Player controller = game.getPlayer(this.getControllerId()); - if (permanent != null - && permanent.isControlledBy(getControllerId()) - && !permanent.getId().equals(this.getSourceId()) - && controller != null - && controller.hasOpponent(event.getPlayerId(), game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever another permanent you control becomes the target of a spell or ability an opponent controls, you may return that permanent to its owner's hand."; - } -} diff --git a/Mage.Sets/src/mage/cards/c/Cowardice.java b/Mage.Sets/src/mage/cards/c/Cowardice.java index b3c8219bc9e..2440ca90769 100644 --- a/Mage.Sets/src/mage/cards/c/Cowardice.java +++ b/Mage.Sets/src/mage/cards/c/Cowardice.java @@ -1,21 +1,16 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author dustinconrad + * @author xenohedron */ public final class Cowardice extends CardImpl { @@ -23,7 +18,7 @@ public final class Cowardice extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}{U}"); // Whenever a creature becomes the target of a spell or ability, return that creature to its owner's hand. - this.addAbility(new CowardiceTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE)); } private Cowardice(final Cowardice card) { @@ -35,39 +30,3 @@ public final class Cowardice extends CardImpl { return new Cowardice(this); } } - -class CowardiceTriggeredAbility extends TriggeredAbilityImpl { - - public CowardiceTriggeredAbility() { - super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), false); - } - - private CowardiceTriggeredAbility(final CowardiceTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature becomes the target of a spell or ability, return that creature to its owner's hand."; - } - - @Override - public CowardiceTriggeredAbility copy() { - return new CowardiceTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java b/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java index 244e13e3987..6121b15037f 100644 --- a/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java +++ b/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java @@ -1,27 +1,25 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; + +import java.util.UUID; /** - * - * @author L_J + * @author xenohedron */ public final class DaruSpiritualist extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.CLERIC, "a Cleric creature you control"); + public DaruSpiritualist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); @@ -31,7 +29,7 @@ public final class DaruSpiritualist extends CardImpl { this.toughness = new MageInt(1); // Whenever a Cleric creature you control becomes the target of a spell or ability, it gets +0/+2 until end of turn. - this.addAbility(new DaruSpiritualistTriggeredAbility(new BoostTargetEffect(0, 2, Duration.EndOfTurn))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new BoostTargetEffect(0, 2, Duration.EndOfTurn), filter)); } private DaruSpiritualist(final DaruSpiritualist card) { @@ -43,39 +41,3 @@ public final class DaruSpiritualist extends CardImpl { return new DaruSpiritualist(this); } } - -class DaruSpiritualistTriggeredAbility extends TriggeredAbilityImpl { - - public DaruSpiritualistTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private DaruSpiritualistTriggeredAbility(final DaruSpiritualistTriggeredAbility ability) { - super(ability); - } - - @Override - public DaruSpiritualistTriggeredAbility copy() { - return new DaruSpiritualistTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && creature.hasSubtype(SubType.CLERIC, game) && creature.getControllerId().equals(getControllerId()) && creature.isCreature(game)) { - this.getEffects().setTargetPointer(new FixedTarget(creature, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Cleric creature you control becomes the target of a spell or ability, it gets +0/+2 until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java index 6ea1e05da49..e4451da52a3 100644 --- a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java +++ b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java @@ -1,29 +1,27 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * @author LevelX2 + * @author xenohedron */ public final class DiffusionSliver extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.SLIVER, "a Sliver creature you control"); + public DiffusionSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.SLIVER); @@ -32,7 +30,8 @@ public final class DiffusionSliver extends CardImpl { this.toughness = new MageInt(1); // Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}. - this.addAbility(new DiffusionSliverTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private DiffusionSliver(final DiffusionSliver card) { @@ -44,48 +43,3 @@ public final class DiffusionSliver extends CardImpl { return new DiffusionSliver(this); } } - -class DiffusionSliverTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.SLIVER); - - DiffusionSliverTriggeredAbility() { - super(Zone.BATTLEFIELD, null); - } - - private DiffusionSliverTriggeredAbility(final DiffusionSliverTriggeredAbility ability) { - super(ability); - } - - @Override - public DiffusionSliverTriggeredAbility copy() { - return new DiffusionSliverTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return false; - } - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature == null || !filter.match(creature, getControllerId(), this, game)) { - return false; - } - this.getEffects().clear(); - Effect effect = new CounterUnlessPaysEffect(new GenericManaCost(2)); - effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - this.addEffect(effect); - return true; - } - - @Override - public String getRule() { - return "Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, " + - "counter that spell or ability unless its controller pays {2}."; - } -} diff --git a/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java b/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java index 8b394ce3735..77509c6b7a9 100644 --- a/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java +++ b/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java @@ -2,18 +2,18 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.ScryTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -25,13 +25,6 @@ import java.util.UUID; */ public final class ElrondMasterOfHealing extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public ElrondMasterOfHealing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -48,8 +41,8 @@ public final class ElrondMasterOfHealing extends CardImpl { .setTargetAdjuster(ElrondMasterOfHealingAdjuster.instance)); // Whenever a creature you control with a +1/+1 counter on it becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), filter, true + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); } diff --git a/Mage.Sets/src/mage/cards/f/FeySteed.java b/Mage.Sets/src/mage/cards/f/FeySteed.java index 9618e16beec..beaecd6abeb 100644 --- a/Mage.Sets/src/mage/cards/f/FeySteed.java +++ b/Mage.Sets/src/mage/cards/f/FeySteed.java @@ -1,28 +1,24 @@ package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** * @@ -30,13 +26,18 @@ import mage.constants.Duration; */ public final class FeySteed extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent( - "another target attacking creature you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target attacking creature you control"); + private static final FilterControlledPermanent filter2 = new FilterControlledPermanent("a creature or planeswalker you control"); static { filter.add(TargetController.YOU.getControllerPredicate()); filter.add(AnotherPredicate.instance); filter.add(AttackingPredicate.instance); + + filter2.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); } public FeySteed(UUID ownerId, CardSetInfo setInfo) { @@ -46,16 +47,15 @@ public final class FeySteed extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // Whenever Fey Steed attacks, another target attacking creature you control - // gains indestructible until end of turn. + // Whenever Fey Steed attacks, another target attacking creature you control gains indestructible until end of turn. Ability ability = new AttacksTriggeredAbility( new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); - // Whenever a creature or planeswalker you control becomes the target of a spell - // or ability an opponent controls, you may draw a card. - this.addAbility(new FeySteedTriggeredAbility()); + // Whenever a creature or planeswalker you control becomes the target of a spell or ability an opponent controls, you may draw a card. + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + filter2, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private FeySteed(final FeySteed card) { @@ -67,44 +67,3 @@ public final class FeySteed extends CardImpl { return new FeySteed(this); } } - -class FeySteedTriggeredAbility extends TriggeredAbilityImpl { - - public FeySteedTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - private FeySteedTriggeredAbility(final FeySteedTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player targetter = game.getPlayer(event.getPlayerId()); - if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { - return false; - } - - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !permanent.isControlledBy(this.getControllerId()) - || (!permanent.isCreature(game) && !permanent.isPlaneswalker(game))) { - return false; - } - return true; - } - - @Override - public String getRule() { - return "Whenever a creature or planeswalker you control becomes the target of a spell or ability an opponent controls, you may draw a card."; - } - - @Override - public FeySteedTriggeredAbility copy() { - return new FeySteedTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java index 267c22f9b85..289d6504247 100644 --- a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java +++ b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java @@ -1,9 +1,7 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -11,15 +9,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author LevelX + * @author xenohedron */ public final class HorobiDeathsWail extends CardImpl { @@ -35,7 +30,7 @@ public final class HorobiDeathsWail extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a creature becomes the target of a spell or ability, destroy that creature. - this.addAbility(new HorobiDeathsWailAbility(new DestroyTargetEffect())); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DestroyTargetEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE)); } private HorobiDeathsWail(final HorobiDeathsWail card) { @@ -48,39 +43,3 @@ public final class HorobiDeathsWail extends CardImpl { } } - -class HorobiDeathsWailAbility extends TriggeredAbilityImpl { - - public HorobiDeathsWailAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private HorobiDeathsWailAbility(final HorobiDeathsWailAbility ability) { - super(ability); - } - - @Override - public HorobiDeathsWailAbility copy() { - return new HorobiDeathsWailAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && creature.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature becomes the target of a spell or ability, destroy that creature."; - } -} diff --git a/Mage.Sets/src/mage/cards/j/JumpTrooper.java b/Mage.Sets/src/mage/cards/j/JumpTrooper.java index c480508d453..becfa48239a 100644 --- a/Mage.Sets/src/mage/cards/j/JumpTrooper.java +++ b/Mage.Sets/src/mage/cards/j/JumpTrooper.java @@ -1,23 +1,18 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreatureCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.TargetStackObject; + +import java.util.UUID; /** * @@ -25,7 +20,7 @@ import mage.target.TargetStackObject; */ public final class JumpTrooper extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("Trooper creatures"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Trooper creature you control"); static { filter.add(SubType.TROOPER.getPredicate()); @@ -38,8 +33,10 @@ public final class JumpTrooper extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or abitlity unless its controller pays {2}. - this.addAbility(new JumpTrooperTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)))); + // Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}. + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)) + .setText("counter that spell or ability unless its controller pays {2}"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } @@ -52,50 +49,3 @@ public final class JumpTrooper extends CardImpl { return new JumpTrooper(this); } } - -class JumpTrooperTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Trooper creature you control"); - - static { - filter.add(SubType.TROOPER.getPredicate()); - } - - public JumpTrooperTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private JumpTrooperTriggeredAbility(final JumpTrooperTriggeredAbility ability) { - super(ability); - } - - @Override - public JumpTrooperTriggeredAbility copy() { - return new JumpTrooperTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}."; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java b/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java index 1d6f0e712b7..c9187f04f7f 100644 --- a/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java +++ b/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java @@ -1,7 +1,7 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.PhaseInTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.PhaseOutTargetEffect; @@ -43,7 +43,7 @@ public final class KingOfTheOathbreakers extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever King of the Oathbreakers or another Spirit you control becomes the target of a spell, it phases out. - this.addAbility(new BecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetAnyTriggeredAbility( new PhaseOutTargetEffect("it"), filter, StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java index a5283caa4ce..8ea82fa23b2 100644 --- a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -3,7 +3,7 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -52,8 +52,8 @@ public final class MilaCraftyCompanion extends ModalDoubleFacedCard { this.getLeftHalfCard().addAbility(new MilaCraftyCompanionTriggeredAbility()); // Whenever a permanent you control becomes the target of a spell or ability and opponent controls, you may draw a card. - this.getLeftHalfCard().addAbility(new BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), true + this.getLeftHalfCard().addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); // 2. diff --git a/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java b/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java index 3a6e44ca46b..858b69eaf18 100644 --- a/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java +++ b/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java @@ -1,22 +1,17 @@ - package mage.cards.s; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.SetTargetPointer; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author TheElk801 + * @author xenohedron */ public final class ShapersSanctuary extends CardImpl { @@ -24,7 +19,9 @@ public final class ShapersSanctuary extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // Whenever a creature you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new CreaturesYouControlTargetedTriggeredAbility(new DrawCardSourceControllerEffect(1))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, + SetTargetPointer.NONE, true)); } private ShapersSanctuary(final ShapersSanctuary card) { @@ -36,42 +33,3 @@ public final class ShapersSanctuary extends CardImpl { return new ShapersSanctuary(this); } } - -class CreaturesYouControlTargetedTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlTargetedTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, true); - } - - private CreaturesYouControlTargetedTriggeredAbility(final CreaturesYouControlTargetedTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlTargetedTriggeredAbility copy() { - return new CreaturesYouControlTargetedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - Player targetter = game.getPlayer(event.getPlayerId()); - if (permanent != null && permanent.isControlledBy(this.getControllerId()) && permanent.isCreature(game)) { - Object object = game.getObject(event.getSourceId()); - if (object != null && targetter != null && targetter.hasOpponent(this.getControllerId(), game)) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of a spell or ability an opponent controls, you may draw a card."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java index 572f31e3cb0..2858c80c79b 100644 --- a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java +++ b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java @@ -1,30 +1,35 @@ package mage.cards.s; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; import mage.game.permanent.token.WolfToken; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * @author fireshoes + * @author xenohedron */ public final class SilverfurPartisan extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("a Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + public SilverfurPartisan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.WOLF); @@ -36,7 +41,8 @@ public final class SilverfurPartisan extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever a Wolf or Werewolf you control becomes the target of an instant or sorcery spell, create a 2/2 green Wolf creature token. - this.addAbility(new CreaturesYouControlBecomesTargetTriggeredAbility(new CreateTokenEffect(new WolfToken()))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CreateTokenEffect(new WolfToken()), + filter, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY)); } private SilverfurPartisan(final SilverfurPartisan card) { @@ -48,54 +54,3 @@ public final class SilverfurPartisan extends CardImpl { return new SilverfurPartisan(this); } } - -class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlBecomesTargetTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private CreaturesYouControlBecomesTargetTriggeredAbility(final CreaturesYouControlBecomesTargetTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlBecomesTargetTriggeredAbility copy() { - return new CreaturesYouControlBecomesTargetTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - if (permanent != null - && object != null - && permanent.isControlledBy(this.controllerId) - && (permanent.hasSubtype(SubType.WOLF, game) - || permanent.hasSubtype(SubType.WEREWOLF, game))) { - if (object instanceof StackObject) { - if (object.isInstant(game) - || object.isSorcery(game)) { - if (getTargets().isEmpty()) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - } - return true; - } - } - } - - return false; - } - - @Override - public String getRule() { - return "Whenever a Wolf or Werewolf you control becomes the target of an instant or sorcery spell, create a 2/2 green Wolf creature token."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SwarmShambler.java b/Mage.Sets/src/mage/cards/s/SwarmShambler.java index d54acb9165d..3f998b5bf48 100644 --- a/Mage.Sets/src/mage/cards/s/SwarmShambler.java +++ b/Mage.Sets/src/mage/cards/s/SwarmShambler.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -13,14 +13,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.counters.CounterType; +import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.game.permanent.token.InsectToken; -import mage.game.stack.Spell; import java.util.UUID; @@ -29,6 +26,11 @@ import java.util.UUID; */ public final class SwarmShambler extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public SwarmShambler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); @@ -44,7 +46,8 @@ public final class SwarmShambler extends CardImpl { )); // Whenever a creature you control with a +1/+1 counter on it becomes the target of a spell an opponent controls, create a 1/1 green Insect creature token. - this.addAbility(new SwarmShamblerTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CreateTokenEffect(new InsectToken()), + StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1, filter)); // {1}, {T}: Put a +1/+1 counter on Swarm Shambler. Ability ability = new SimpleActivatedAbility( @@ -63,40 +66,3 @@ public final class SwarmShambler extends CardImpl { return new SwarmShambler(this); } } - -class SwarmShamblerTriggeredAbility extends TriggeredAbilityImpl { - - SwarmShamblerTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new InsectToken()), false); - } - - private SwarmShamblerTriggeredAbility(final SwarmShamblerTriggeredAbility ability) { - super(ability); - } - - @Override - public SwarmShamblerTriggeredAbility copy() { - return new SwarmShamblerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Spell sourceObject = game.getSpell(event.getSourceId()); - Permanent permanent = game.getPermanent(event.getTargetId()); - return sourceObject != null - && permanent != null - && StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1.match(permanent, getControllerId(), this, game) - && StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS.match(sourceObject, getControllerId(), this, game); - } - - @Override - public String getRule() { - return "Whenever a creature you control with a +1/+1 counter on it " + - "becomes the target of a spell an opponent controls, create a 1/1 green Insect creature token."; - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java index c5e4f80ffaa..54a2d11e2ea 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java +++ b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java @@ -1,29 +1,29 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author xenohedron */ public final class ThunderbreakRegent extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Dragon you control"); + static { + filter.add(SubType.DRAGON.getPredicate()); + } + public ThunderbreakRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); this.subtype.add(SubType.DRAGON); @@ -34,7 +34,8 @@ public final class ThunderbreakRegent extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a Dragon you control becomes the target of a spell or ability your opponent controls, Thunderbreak Regent deals 3 damage to that player. - this.addAbility(new ThunderbreakRegentTriggeredAbility(new DamageTargetEffect(3))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DamageTargetEffect(3), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PLAYER, false)); } private ThunderbreakRegent(final ThunderbreakRegent card) { @@ -46,49 +47,3 @@ public final class ThunderbreakRegent extends CardImpl { return new ThunderbreakRegent(this); } } - -class ThunderbreakRegentTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Sliver creature you control"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - public ThunderbreakRegentTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private ThunderbreakRegentTriggeredAbility(final ThunderbreakRegentTriggeredAbility ability) { - super(ability); - } - - @Override - public ThunderbreakRegentTriggeredAbility copy() { - return new ThunderbreakRegentTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, {this} deals 3 damage to that player."; - } -} diff --git a/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java b/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java index 3f65f7521cf..f9ecc19d3b7 100644 --- a/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java +++ b/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java @@ -1,28 +1,22 @@ package mage.cards.v; -import java.util.UUID; - import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.counter.AddPoisonCounterTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.ToxicAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.TargetPermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * @author TheElk801 + * @author xenohedron */ public final class VeneratedRotpriest extends CardImpl { @@ -38,7 +32,10 @@ public final class VeneratedRotpriest extends CardImpl { this.addAbility(new ToxicAbility(1)); // Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter. - this.addAbility(new VeneratedRotpriestTriggeredAbility()); + Ability ability = new BecomesTargetAnyTriggeredAbility(new AddPoisonCounterTargetEffect(1), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, false); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); } private VeneratedRotpriest(final VeneratedRotpriest card) { @@ -50,41 +47,3 @@ public final class VeneratedRotpriest extends CardImpl { return new VeneratedRotpriest(this); } } - -class VeneratedRotpriestTriggeredAbility extends TriggeredAbilityImpl { - - VeneratedRotpriestTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddPoisonCounterTargetEffect(1)); - this.addTarget(new TargetOpponent()); - } - - private VeneratedRotpriestTriggeredAbility(final VeneratedRotpriestTriggeredAbility ability) { - super(ability); - } - - @Override - public VeneratedRotpriestTriggeredAbility copy() { - return new VeneratedRotpriestTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - return permanent != null - && object != null - && isControlledBy(permanent.getControllerId()) - && permanent.isCreature(game) - && object instanceof Spell; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter."; - } -} diff --git a/Mage.Sets/src/mage/cards/w/WildDefiance.java b/Mage.Sets/src/mage/cards/w/WildDefiance.java index ee74a5bba37..902a70a70e6 100644 --- a/Mage.Sets/src/mage/cards/w/WildDefiance.java +++ b/Mage.Sets/src/mage/cards/w/WildDefiance.java @@ -1,24 +1,17 @@ package mage.cards.w; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * @author noxx + * @author xenohedron */ public final class WildDefiance extends CardImpl { @@ -26,7 +19,8 @@ public final class WildDefiance extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn. - this.addAbility(new CreaturesYouControlBecomesTargetTriggeredAbility(new BoostTargetEffect(3, 3, Duration.EndOfTurn))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new BoostTargetEffect(3, 3, Duration.EndOfTurn), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY)); } private WildDefiance(final WildDefiance card) { @@ -38,51 +32,3 @@ public final class WildDefiance extends CardImpl { return new WildDefiance(this); } } - -class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlBecomesTargetTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private CreaturesYouControlBecomesTargetTriggeredAbility(final CreaturesYouControlBecomesTargetTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlBecomesTargetTriggeredAbility copy() { - return new CreaturesYouControlBecomesTargetTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.isControlledBy(this.controllerId) - && permanent.isCreature(game)) { - MageObject object = game.getObject(event.getSourceId()); - if (object instanceof Spell) { - Card c = (Spell) object; - if (c.isInstantOrSorcery(game)) { - if (getTargets().isEmpty()) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn."; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java new file mode 100644 index 00000000000..0a4917cf4f4 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java @@ -0,0 +1,97 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class BecomesTargetAnyTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filterTarget; + private final FilterStackObject filterStack; + private final SetTargetPointer setTargetPointer; + + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget) { + this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A); + } + + /** + * "Whenever [a filterTarget] becomes the target of [a filterStack], [effect]" + * @param effect defaults to SetTargetPointer.PERMANENT + * @param filterTarget permanents to check being targetted + * @param filterStack spells/abilities to check targeting + */ + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) { + this(effect, filterTarget, filterStack, SetTargetPointer.PERMANENT, false); + } + + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, + SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filterTarget = filterTarget; + this.filterStack = filterStack; + this.setTargetPointer = setTargetPointer; + setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of " + + filterStack.getMessage() + ", "); + } + + protected BecomesTargetAnyTriggeredAbility(final BecomesTargetAnyTriggeredAbility ability) { + super(ability); + this.filterTarget = ability.filterTarget; + this.filterStack = ability.filterStack; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public BecomesTargetAnyTriggeredAbility copy() { + return new BecomesTargetAnyTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || !filterTarget.match(permanent, getControllerId(), this, game)) { + return false; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case PERMANENT: + this.getAllEffects().setTargetPointer(new FixedTarget(permanent.getId(), game)); + break; + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); + break; + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAnyTriggeredAbility"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java deleted file mode 100644 index 598ee6ad0a3..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java +++ /dev/null @@ -1,83 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.players.Player; - -import java.util.*; - -/** - * @author weirddan455 - */ -public class BecomesTargetOpponentAllTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterPermanent filter; - - public BecomesTargetOpponentAllTriggeredAbility(Effect effect, boolean optional) { - this(effect, StaticFilters.FILTER_CONTROLLED_A_PERMANENT, optional); - } - - public BecomesTargetOpponentAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filter = filter; - setTriggerPhrase("Whenever " + filter + " becomes the target of a spell or ability an opponent controls, "); - } - - private BecomesTargetOpponentAllTriggeredAbility(final BecomesTargetOpponentAllTriggeredAbility ability) { - super(ability); - this.filter = ability.filter; - } - - @Override - public BecomesTargetOpponentAllTriggeredAbility copy() { - return new BecomesTargetOpponentAllTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (sourceObject == null) { - return false; - } - Player targetter = game.getPlayer(event.getPlayerId()); - if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { - return false; - } - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) { - return false; - } - // If a spell or ability an opponent controls targets a single permanent you control more than once, - // Battle Mammoth’s triggered ability will trigger only once. - // However, if a spell or ability an opponent controls targets multiple permanents you control, - // Battle Mammoth’s triggered ability will trigger once for each of those permanents. - - // targetMap - key - targetId, value - Set of stackObject Ids - Map> targetMap = (Map>) game.getState().getValue("targetMap" + this.id); - if (targetMap == null) { - targetMap = new HashMap<>(); - } - Set sourceObjects = targetMap.get(event.getTargetId()); - if (sourceObjects == null) { - sourceObjects = new HashSet<>(); - } - if (!sourceObjects.add(sourceObject.getId())) { - return false; - } - targetMap.put(event.getTargetId(), sourceObjects); - game.getState().setValue("targetMap" + this.id, targetMap); - return true; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java deleted file mode 100644 index cbd330ebb13..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ /dev/null @@ -1,73 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.FilterStackObject; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; - -/** - * @author Susucr - */ -public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterPermanent filterTarget; - private final FilterStackObject filterStack; - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget) { - this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A); - } - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) { - this(effect, filterTarget, filterStack, false); - } - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, - boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filterTarget = filterTarget; - this.filterStack = filterStack; - setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of " - + filterStack.getMessage() + ", "); - } - - protected BecomesTargetTriggeredAbility(final BecomesTargetTriggeredAbility ability) { - super(ability); - this.filterTarget = ability.filterTarget; - this.filterStack = ability.filterStack; - } - - @Override - public BecomesTargetTriggeredAbility copy() { - return new BecomesTargetTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (sourceObject == null - || !filterStack.match(sourceObject, getControllerId(), this, game)) { - return false; - } - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null - || !filterTarget.match(permanent, getControllerId(), this, game)) { - return false; - } - - getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - - return true; - } -} From 4ab36880fe75c7ca8f4f96b55d74067163b7f857 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:20:12 -0400 Subject: [PATCH 07/18] new common BecomesTargetSourceFirstTimeTriggeredAbility --- Mage.Sets/src/mage/cards/a/AngelicCub.java | 54 +++------------ Mage.Sets/src/mage/cards/g/GlyphKeeper.java | 67 +++--------------- .../src/mage/cards/j/JettingGlasskite.java | 63 +++-------------- .../mage/cards/k/KiraGreatGlassSpinner.java | 68 +++---------------- .../src/mage/cards/s/ShimmeringGlasskite.java | 64 +++-------------- ...TargetSourceFirstTimeTriggeredAbility.java | 41 +++++++++++ ...rOfTimesPermanentTargetedATurnWatcher.java | 38 +++++------ 7 files changed, 108 insertions(+), 287 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/a/AngelicCub.java b/Mage.Sets/src/mage/cards/a/AngelicCub.java index 460320f9aaa..a95f7ccb184 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicCub.java +++ b/Mage.Sets/src/mage/cards/a/AngelicCub.java @@ -1,7 +1,7 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -12,18 +12,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; import java.util.UUID; /** - * @author AustinYQM + * @author AustinYQM, xenohedron */ public final class AngelicCub extends CardImpl { @@ -36,7 +33,10 @@ public final class AngelicCub extends CardImpl { this.toughness = new MageInt(1); // Whenever Angelic Cub becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it. - this.addAbility(new AngelicCubAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false + )); // As long as Angelic Cub has three or more +1/+1 counters on it, it has flying. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.P1P1, 3), "As long as {this} has three or more +1/+1 counters on it, it has flying."))); } @@ -50,41 +50,3 @@ public final class AngelicCub extends CardImpl { return new AngelicCub(this); } } - -class AngelicCubAbility extends TriggeredAbilityImpl { - - public AngelicCubAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - public AngelicCubAbility(final mage.cards.a.AngelicCubAbility ability) { - super(ability); - } - - @Override - public mage.cards.a.AngelicCubAbility copy() { - return new mage.cards.a.AngelicCubAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - return watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game); - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java index 2644ded03be..5572b1f7ec4 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java +++ b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java @@ -1,28 +1,23 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.EmbalmAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author Styxo + * @author Styxo, xenohedron */ public final class GlyphKeeper extends CardImpl { @@ -37,7 +32,10 @@ public final class GlyphKeeper extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Glyph Keeper becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new GlyphKeeperAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); // Embalm {5}{U}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl<>("{5}{U}{U}"), this)); @@ -53,48 +51,3 @@ public final class GlyphKeeper extends CardImpl { return new GlyphKeeper(this); } } - -class GlyphKeeperAbility extends TriggeredAbilityImpl { - - public GlyphKeeperAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private GlyphKeeperAbility(final GlyphKeeperAbility ability) { - super(ability); - } - - @Override - public GlyphKeeperAbility copy() { - return new GlyphKeeperAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null - && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java index 419194b95ed..ab68e05e1fe 100644 --- a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java +++ b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class JettingGlasskite extends CardImpl { @@ -34,7 +31,10 @@ public final class JettingGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Jetting Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new JettingGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +47,3 @@ public final class JettingGlasskite extends CardImpl { return new JettingGlasskite(this); } } - -class JettingGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public JettingGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private JettingGlasskiteAbility(final JettingGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public JettingGlasskiteAbility copy() { - return new JettingGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java index ad727125a6b..09faf71ee3b 100644 --- a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java +++ b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java @@ -1,10 +1,9 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.FlyingAbility; @@ -12,11 +11,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +import java.util.UUID; /** * @@ -36,12 +32,12 @@ public final class KiraGreatGlassSpinner extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability." - Effect effect = new CounterTargetEffect(); - effect.setText("counter that spell or ability"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(new KiraGreatGlassSpinnerAbility(effect), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURES)), - new NumberOfTimesPermanentTargetedATurnWatcher()); + TriggeredAbility gainedAbility = new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + ).setTriggerPhrase("Whenever this creature becomes the target of a spell or ability for the first time each turn, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + gainedAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES))); } @@ -54,47 +50,3 @@ public final class KiraGreatGlassSpinner extends CardImpl { return new KiraGreatGlassSpinner(this); } } - -class KiraGreatGlassSpinnerAbility extends TriggeredAbilityImpl { - - public KiraGreatGlassSpinnerAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - private KiraGreatGlassSpinnerAbility(final KiraGreatGlassSpinnerAbility ability) { - super(ability); - } - - @Override - public KiraGreatGlassSpinnerAbility copy() { - return new KiraGreatGlassSpinnerAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java index c2e8b828d92..c32f0be66df 100644 --- a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java +++ b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class ShimmeringGlasskite extends CardImpl { @@ -26,7 +23,6 @@ public final class ShimmeringGlasskite extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); this.subtype.add(SubType.SPIRIT); - this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -34,7 +30,10 @@ public final class ShimmeringGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Shimmering Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new ShimmeringGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +46,3 @@ public final class ShimmeringGlasskite extends CardImpl { return new ShimmeringGlasskite(this); } } - -class ShimmeringGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public ShimmeringGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private ShimmeringGlasskiteAbility(final ShimmeringGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public ShimmeringGlasskiteAbility copy() { - return new ShimmeringGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java new file mode 100644 index 00000000000..bf895161c74 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java @@ -0,0 +1,41 @@ +package mage.abilities.common; + +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterStackObject; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +/** + * @author xenohedron + */ +public class BecomesTargetSourceFirstTimeTriggeredAbility extends BecomesTargetSourceTriggeredAbility { + + public BecomesTargetSourceFirstTimeTriggeredAbility(Effect effect, FilterStackObject filter, + SetTargetPointer setTargetPointer, boolean optional) { + super(effect, filter, setTargetPointer, optional); + this.addWatcher(new NumberOfTimesPermanentTargetedATurnWatcher()); + setTriggerPhrase("Whenever {this} becomes the target of " + filter.getMessage() + " for the first time each turn, "); + } + + protected BecomesTargetSourceFirstTimeTriggeredAbility(final BecomesTargetSourceFirstTimeTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesTargetSourceFirstTimeTriggeredAbility copy() { + return new BecomesTargetSourceFirstTimeTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); + if (permanent == null || watcher == null || watcher.numTimesTargetedThisTurn(permanent, game) > 1) { + return false; + } + return super.checkTrigger(event, game); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index 1df8fa84d1f..55e1b0419e0 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -1,16 +1,17 @@ - - package mage.watchers.common; -import java.util.HashMap; -import java.util.Map; import mage.MageObjectReference; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; + /** * * @author LevelX2 @@ -25,24 +26,23 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TARGETED) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - MageObjectReference mor = new MageObjectReference(permanent, game); - int amount = 0; - if (permanentsTargeted.containsKey(mor)) { - amount = permanentsTargeted.get(mor); - } - permanentsTargeted.put(mor, ++amount); - } + if (event.getType() != GameEvent.EventType.TARGETED) { + return; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !CardUtil.checkTargetMap(null, targetingObject, event, game)) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + MageObjectReference mor = new MageObjectReference(permanent, game); + int nTimes = permanentsTargeted.computeIfAbsent(mor, k -> 0); + permanentsTargeted.put(mor, nTimes + 1); } } - public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) { - if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) { - return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2; - } - return true; + public int numTimesTargetedThisTurn(Permanent permanent, Game game) { + return permanentsTargeted.computeIfAbsent(new MageObjectReference(permanent, game), k -> 0); } @Override From d83d27dfa3067ff0d35cdcec358b68d8ce4d2024 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:33:49 -0400 Subject: [PATCH 08/18] update SourceBecomesTargetTriggeredAbility with proper logic and trigger phrase generation --- .../src/mage/cards/b/BonecrusherGiant.java | 3 +- .../src/mage/cards/w/WardenOfTheWoods.java | 3 +- .../SourceBecomesTargetTriggeredAbility.java | 42 +++++++++++-------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java index 33c743ba440..ace07c97f3a 100644 --- a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java +++ b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java @@ -31,8 +31,7 @@ public final class BonecrusherGiant extends AdventureCard { this.addAbility(new SourceBecomesTargetTriggeredAbility( new DamageTargetEffect( 2, true, "that spell's controller", "{this}" - ), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER - ).setTriggerPhrase("Whenever {this} becomes the target of a spell, ")); + ), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false)); // Stomp // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java index 8597e1b07d6..6a4c068ce26 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java @@ -32,8 +32,7 @@ public final class WardenOfTheWoods extends CardImpl { this.addAbility(new SourceBecomesTargetTriggeredAbility( new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, - SetTargetPointer.NONE, true - ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability an opponent controls, ")); + SetTargetPointer.NONE, true)); } private WardenOfTheWoods(final WardenOfTheWoods card) { diff --git a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java index bb0c7ebb592..6c2aa3d210a 100644 --- a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java @@ -2,6 +2,10 @@ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterStackObject; @@ -10,6 +14,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author North @@ -24,18 +29,18 @@ public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { } public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) { - this(effect, filter, SetTargetPointer.NONE); - } - - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) { - this(effect, filter, setTargetPointer, false); + this(effect, filter, SetTargetPointer.NONE, false); } public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; this.setTargetPointer = setTargetPointer; - setTriggerPhrase("When {this} becomes the target of " + filter.getMessage() + ", "); + boolean textWhen = (effect instanceof SacrificeSourceEffect + || effect instanceof ReturnToHandSourceEffect + || effect instanceof ShuffleIntoLibrarySourceEffect + || effect instanceof ExileSourceEffect); + setTriggerPhrase((textWhen ? "When" : "Whenever") + " {this} becomes the target of " + filter.getMessage() + ", "); } protected SourceBecomesTargetTriggeredAbility(final SourceBecomesTargetTriggeredAbility ability) { @@ -56,24 +61,27 @@ public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (!event.getTargetId().equals(getSourceId()) - || !filter.match(sourceObject, getControllerId(), this, game)) { + if (!event.getTargetId().equals(getSourceId())) { + return false; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { return false; } switch (setTargetPointer) { case PLAYER: - this.getEffects().stream() - .forEach(effect -> effect.setTargetPointer( - new FixedTarget(sourceObject.getControllerId(), game) - )); + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); break; case SPELL: - this.getEffects().stream() - .forEach(effect -> effect.setTargetPointer( - new FixedTarget(sourceObject.getId(), game) - )); + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetSourceTriggeredAbility"); } return true; } From 0cbee152d239634e2b93fc6e2968af8dacba7e95 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:36:05 -0400 Subject: [PATCH 09/18] reimplement with common class --- Mage.Sets/src/mage/cards/f/FrostTitan.java | 57 +++----------- Mage.Sets/src/mage/cards/o/OpalineSliver.java | 75 +++++-------------- .../src/mage/cards/r/RealitySmasher.java | 70 ++++------------- 3 files changed, 42 insertions(+), 160 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/FrostTitan.java b/Mage.Sets/src/mage/cards/f/FrostTitan.java index ca75d0dcd1d..b718cf0e286 100644 --- a/Mage.Sets/src/mage/cards/f/FrostTitan.java +++ b/Mage.Sets/src/mage/cards/f/FrostTitan.java @@ -1,11 +1,9 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.SourceBecomesTargetTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; @@ -13,13 +11,12 @@ import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import mage.target.TargetStackObject; + +import java.util.UUID; /** * @@ -35,7 +32,10 @@ public final class FrostTitan extends CardImpl { this.toughness = new MageInt(6); // Whenever Frost Titan becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays 2. - this.addAbility(new FrostTitanAbility()); + this.addAbility(new SourceBecomesTargetTriggeredAbility( + new CounterUnlessPaysEffect(new GenericManaCost(2)).setText("counter that spell or ability unless its controller pays {2}"), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false + )); // Whenever Frost Titan enters the battlefield or attacks, tap target permanent. It doesn't untap during its controller's next untap step. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new TapTargetEffect()); @@ -54,42 +54,3 @@ public final class FrostTitan extends CardImpl { } } - -class FrostTitanAbility extends TriggeredAbilityImpl { - - public FrostTitanAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(2)), false); - } - - private FrostTitanAbility(final FrostTitanAbility ability) { - super(ability); - } - - @Override - public FrostTitanAbility copy() { - return new FrostTitanAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}."; - } - -} diff --git a/Mage.Sets/src/mage/cards/o/OpalineSliver.java b/Mage.Sets/src/mage/cards/o/OpalineSliver.java index 2ce3e6c9083..fd59e9c43e0 100644 --- a/Mage.Sets/src/mage/cards/o/OpalineSliver.java +++ b/Mage.Sets/src/mage/cards/o/OpalineSliver.java @@ -1,34 +1,30 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SourceBecomesTargetTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.StackObject; +import mage.filter.FilterSpell; + +import java.util.UUID; /** - * - * @author anonymous + * @author xenohedron */ public final class OpalineSliver extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("All Slivers"); + private static final FilterPermanent filterSliver = new FilterPermanent("All Slivers"); + private static final FilterSpell filterSpell = new FilterSpell("a spell an opponent controls"); static { - filter.add(SubType.SLIVER.getPredicate()); + filterSliver.add(SubType.SLIVER.getPredicate()); + filterSpell.add(TargetController.OPPONENT.getControllerPredicate()); } public OpalineSliver(UUID ownerId, CardSetInfo setInfo) { @@ -38,9 +34,12 @@ public final class OpalineSliver extends CardImpl { this.toughness = new MageInt(2); // All Slivers have "Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( - new OpalineSliverTriggeredAbility(), Duration.WhileOnBattlefield, - filter, "All Slivers have \"Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card.\""))); + Ability gainedTriggeredAbility = new SourceBecomesTargetTriggeredAbility( + new DrawCardSourceControllerEffect(1), filterSpell, SetTargetPointer.NONE, true) + .setTriggerPhrase("Whenever this permanent becomes the target of a spell an opponent controls, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + gainedTriggeredAbility, Duration.WhileOnBattlefield, filterSliver, + "All Slivers have \"Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card.\""))); } private OpalineSliver(final OpalineSliver card) { @@ -52,43 +51,3 @@ public final class OpalineSliver extends CardImpl { return new OpalineSliver(this); } } - -class OpalineSliverTriggeredAbility extends TriggeredAbilityImpl { - - public OpalineSliverTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); - } - - private OpalineSliverTriggeredAbility(final OpalineSliverTriggeredAbility ability) { - super(ability); - } - - @Override - public OpalineSliverTriggeredAbility copy() { - return new OpalineSliverTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - - if (spell == null) { - return false; - } else { - return event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.controllerId).contains(event.getPlayerId()) - && StaticFilters.FILTER_SPELL_A.match(spell, getControllerId(), this, game); - } - } - - @Override - public String getRule() { - return "Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card."; - } - -} diff --git a/Mage.Sets/src/mage/cards/r/RealitySmasher.java b/Mage.Sets/src/mage/cards/r/RealitySmasher.java index 35d367dea82..55c441ce5ff 100644 --- a/Mage.Sets/src/mage/cards/r/RealitySmasher.java +++ b/Mage.Sets/src/mage/cards/r/RealitySmasher.java @@ -1,9 +1,7 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SourceBecomesTargetTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.HasteAbility; @@ -11,21 +9,24 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; +import mage.constants.TargetController; +import mage.filter.FilterSpell; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class RealitySmasher extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public RealitySmasher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{C}"); this.subtype.add(SubType.ELDRAZI); @@ -37,7 +38,10 @@ public final class RealitySmasher extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); // Whenever Reality Smasher becomes the target of a spell an opponent controls, counter that spell unless its controller discards a card. - this.addAbility(new RealitySmasherTriggeredAbility()); + this.addAbility(new SourceBecomesTargetTriggeredAbility( + new CounterUnlessPaysEffect(new DiscardCardCost()).setText("counter that spell unless its controller discards a card"), + filter, SetTargetPointer.SPELL, false + )); } private RealitySmasher(final RealitySmasher card) { @@ -49,45 +53,3 @@ public final class RealitySmasher extends CardImpl { return new RealitySmasher(this); } } - -class RealitySmasherTriggeredAbility extends TriggeredAbilityImpl { - - public RealitySmasherTriggeredAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new DiscardCardCost()), false); - } - - private RealitySmasherTriggeredAbility(final RealitySmasherTriggeredAbility ability) { - super(ability); - } - - @Override - public RealitySmasherTriggeredAbility copy() { - return new RealitySmasherTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - if (!(spell instanceof Spell)) { - return false; - } else { - if (event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - getEffects().setTargetPointer(new FixedTarget(spell.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell an opponent controls, counter that spell unless its controller discards a card."; - } - -} From 8f92a2330202402a830ed33082d7bd9393bf01c3 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:38:22 -0400 Subject: [PATCH 10/18] rename class for consistency --- Mage.Sets/src/mage/cards/a/AngelicProtector.java | 4 ++-- Mage.Sets/src/mage/cards/b/BonecrusherGiant.java | 4 ++-- Mage.Sets/src/mage/cards/b/BoneshardSlasher.java | 4 ++-- Mage.Sets/src/mage/cards/c/CephalidAristocrat.java | 4 ++-- .../src/mage/cards/c/CephalidIllusionist.java | 4 ++-- .../src/mage/cards/c/CrystallineNautilus.java | 6 +++--- Mage.Sets/src/mage/cards/c/CursedMonstrosity.java | 4 ++-- Mage.Sets/src/mage/cards/d/DepartedDeckhand.java | 4 ++-- Mage.Sets/src/mage/cards/d/DismissIntoDream.java | 4 ++-- Mage.Sets/src/mage/cards/d/DreamStrix.java | 4 ++-- Mage.Sets/src/mage/cards/f/ForceProjection.java | 4 ++-- Mage.Sets/src/mage/cards/f/FrostTitan.java | 4 ++-- Mage.Sets/src/mage/cards/f/FrostWalker.java | 4 ++-- Mage.Sets/src/mage/cards/f/FugitiveDruid.java | 4 ++-- Mage.Sets/src/mage/cards/g/GossamerPhantasm.java | 4 ++-- .../src/mage/cards/i/IlluminatorVirtuoso.java | 4 ++-- Mage.Sets/src/mage/cards/i/IllusionaryServant.java | 4 ++-- Mage.Sets/src/mage/cards/l/LivewireLash.java | 4 ++-- Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java | 4 ++-- Mage.Sets/src/mage/cards/m/Mirozel.java | 4 ++-- Mage.Sets/src/mage/cards/m/MyriadConstruct.java | 4 ++-- Mage.Sets/src/mage/cards/o/OpalineSliver.java | 4 ++-- Mage.Sets/src/mage/cards/p/Petrahydrox.java | 4 ++-- .../src/mage/cards/p/PhantasmalAbomination.java | 4 ++-- Mage.Sets/src/mage/cards/p/PhantasmalBear.java | 4 ++-- Mage.Sets/src/mage/cards/p/PhantasmalDragon.java | 4 ++-- Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java | 4 ++-- Mage.Sets/src/mage/cards/p/PhantasmalImage.java | 4 ++-- Mage.Sets/src/mage/cards/p/PhantomBeast.java | 4 ++-- Mage.Sets/src/mage/cards/r/RealitySmasher.java | 4 ++-- Mage.Sets/src/mage/cards/s/SegmentedWurm.java | 4 ++-- Mage.Sets/src/mage/cards/s/SkulkingFugitive.java | 4 ++-- Mage.Sets/src/mage/cards/s/SkulkingGhost.java | 4 ++-- Mage.Sets/src/mage/cards/s/SkulkingKnight.java | 4 ++-- Mage.Sets/src/mage/cards/s/StormchaserDrake.java | 4 ++-- Mage.Sets/src/mage/cards/t/TarPitWarrior.java | 4 ++-- Mage.Sets/src/mage/cards/t/TaskForce.java | 4 ++-- Mage.Sets/src/mage/cards/t/TetheredSkirge.java | 4 ++-- .../src/mage/cards/t/TheHowlingAbomination.java | 4 ++-- Mage.Sets/src/mage/cards/t/ThornLieutenant.java | 4 ++-- .../src/mage/cards/t/TreacherousBlessing.java | 4 ++-- Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java | 4 ++-- .../mage/test/cards/copy/PhantasmalImageTest.java | 6 +++--- ...va => BecomesTargetSourceTriggeredAbility.java} | 14 +++++++------- 44 files changed, 95 insertions(+), 95 deletions(-) rename Mage/src/main/java/mage/abilities/common/{SourceBecomesTargetTriggeredAbility.java => BecomesTargetSourceTriggeredAbility.java} (86%) diff --git a/Mage.Sets/src/mage/cards/a/AngelicProtector.java b/Mage.Sets/src/mage/cards/a/AngelicProtector.java index ac598d01cca..045fcd3869f 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicProtector.java +++ b/Mage.Sets/src/mage/cards/a/AngelicProtector.java @@ -1,7 +1,7 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class AngelicProtector extends CardImpl { this.toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new BoostSourceEffect(0, 3, Duration.EndOfTurn) ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability, ")); } diff --git a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java index ace07c97f3a..89db9bb7b8c 100644 --- a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java +++ b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java @@ -1,7 +1,7 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.DamageCantBePreventedEffect; import mage.cards.AdventureCard; @@ -28,7 +28,7 @@ public final class BonecrusherGiant extends AdventureCard { this.toughness = new MageInt(3); // Whenever Bonecrusher Giant becomes the target of a spell, Bonecrusher Giant deals 2 damage to that spell's controller. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DamageTargetEffect( 2, true, "that spell's controller", "{this}" ), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false)); diff --git a/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java b/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java index f3d65493a77..201c7cd21ee 100644 --- a/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java +++ b/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -35,7 +35,7 @@ public final class BoneshardSlasher extends CardImpl { Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveyardCondition(7), "As long as seven or more cards are in your graveyard, {this} gets +2/+2")); - Effect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())), + Effect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())), new CardsInControllerGraveyardCondition(7), "and has \"When {this} becomes the target of a spell or ability, sacrifice it.\""); ability.addEffect(effect); ability.setAbilityWord(AbilityWord.THRESHOLD); diff --git a/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java b/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java index 15cc200f35e..e3ada73de32 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java +++ b/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,7 +22,7 @@ public final class CephalidAristocrat extends CardImpl { this.toughness = new MageInt(3); // Whenever Cephalid Aristocrat becomes the target of a spell or ability, put the top two cards of your library into your graveyard. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new MillCardsControllerEffect(2))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new MillCardsControllerEffect(2))); } private CephalidAristocrat(final CephalidAristocrat card) { diff --git a/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java b/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java index e8f205d8d5c..966d3f3fa40 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java +++ b/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -35,7 +35,7 @@ public final class CephalidIllusionist extends CardImpl { this.toughness = new MageInt(1); // Whenever Cephalid Illusionist becomes the target of a spell or ability, put the top three cards of your library into your graveyard. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new MillCardsControllerEffect(3))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new MillCardsControllerEffect(3))); // {2}{U}, {tap}: Prevent all combat damage that would be dealt to Effect effect = new PreventDamageToTargetEffect(Duration.EndOfTurn, true); diff --git a/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java b/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java index 5e5f14cbf69..bedc6207572 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java +++ b/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -36,13 +36,13 @@ public final class CrystallineNautilus extends CardImpl { this.addAbility(new BestowAbility(this, "{3}{U}{U}")); // When Crystalline Nautilus becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); // Enchanted creature gets +4/+4 and has "When this creature becomes the target of a spell or ability, sacrifice it." Effect effect = new BoostEnchantedEffect(4,4,Duration.WhileOnBattlefield); effect.setText("Enchanted creature gets +4/+4"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - effect = new GainAbilityAttachedEffect(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect()), AttachmentType.AURA, Duration.WhileOnBattlefield); + effect = new GainAbilityAttachedEffect(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect()), AttachmentType.AURA, Duration.WhileOnBattlefield); effect.setText("and has \"When this creature becomes the target of a spell or ability, sacrifice it.\""); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java b/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java index 15eccc15810..18ae9a7b85f 100644 --- a/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java +++ b/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.keyword.FlyingAbility; @@ -30,7 +30,7 @@ public final class CursedMonstrosity extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Cursed Monstrosity becomes the target of a spell or ability, sacrifice it unless you discard a land card. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceUnlessPaysEffect( new DiscardTargetCost(new TargetCardInHand(new FilterLandCard())) ))); diff --git a/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java b/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java index 19b0a9b6fbc..2eccac06366 100644 --- a/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java +++ b/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -43,7 +43,7 @@ public final class DepartedDeckhand extends CardImpl { this.toughness = new MageInt(2); // When Departed Deckhand becomes the target of a spell, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java index 70340d11ffe..b43d6514179 100644 --- a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java +++ b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; @@ -72,7 +72,7 @@ class DismissIntoDreamEffect extends CreaturesBecomeOtherTypeEffect { if (layer == Layer.AbilityAddingRemovingEffects_6) { for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { - object.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); + object.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/d/DreamStrix.java b/Mage.Sets/src/mage/cards/d/DreamStrix.java index 54dd78d5476..556e2a57d6c 100644 --- a/Mage.Sets/src/mage/cards/d/DreamStrix.java +++ b/Mage.Sets/src/mage/cards/d/DreamStrix.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -32,7 +32,7 @@ public final class DreamStrix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Strix becomes the target of a spell, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/f/ForceProjection.java b/Mage.Sets/src/mage/cards/f/ForceProjection.java index 511431b16f5..5b1b4cb44ce 100644 --- a/Mage.Sets/src/mage/cards/f/ForceProjection.java +++ b/Mage.Sets/src/mage/cards/f/ForceProjection.java @@ -2,7 +2,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -76,7 +76,7 @@ class ForceProjectionEffect extends OneShotEffect { effect.setAdditionalSubType(SubType.SPIRIT); // and gains "When this creature becomes the target of a spell, sacrifice it." - effect.addAdditionalAbilities(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect(), new FilterSpell())); + effect.addAdditionalAbilities(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect(), new FilterSpell())); return effect.apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/f/FrostTitan.java b/Mage.Sets/src/mage/cards/f/FrostTitan.java index b718cf0e286..1647ffe9ad2 100644 --- a/Mage.Sets/src/mage/cards/f/FrostTitan.java +++ b/Mage.Sets/src/mage/cards/f/FrostTitan.java @@ -3,7 +3,7 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; @@ -32,7 +32,7 @@ public final class FrostTitan extends CardImpl { this.toughness = new MageInt(6); // Whenever Frost Titan becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays 2. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new CounterUnlessPaysEffect(new GenericManaCost(2)).setText("counter that spell or ability unless its controller pays {2}"), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false )); diff --git a/Mage.Sets/src/mage/cards/f/FrostWalker.java b/Mage.Sets/src/mage/cards/f/FrostWalker.java index b144919055d..e4567b79d1c 100644 --- a/Mage.Sets/src/mage/cards/f/FrostWalker.java +++ b/Mage.Sets/src/mage/cards/f/FrostWalker.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class FrostWalker extends CardImpl { this.toughness = new MageInt(1); // When Frost Walker becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private FrostWalker(final FrostWalker card) { diff --git a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java index 69da20040fb..aaf01e3282c 100644 --- a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java +++ b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,7 +31,7 @@ public final class FugitiveDruid extends CardImpl { this.toughness = new MageInt(2); // Whenever Fugitive Druid becomes the target of an Aura spell, you draw a card. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new DrawCardSourceControllerEffect(1), filter)); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), filter)); } private FugitiveDruid(final FugitiveDruid card) { diff --git a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java index 12377566179..b021f75f549 100644 --- a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java +++ b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java @@ -1,7 +1,7 @@ package mage.cards.g; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class GossamerPhantasm extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Gossamer Phantasm becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private GossamerPhantasm(final GossamerPhantasm card) { diff --git a/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java b/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java index 8d35d096209..d6887218a3b 100644 --- a/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java +++ b/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java @@ -1,7 +1,7 @@ package mage.cards.i; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.keyword.ConniveSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class IlluminatorVirtuoso extends CardImpl { this.addAbility(DoubleStrikeAbility.getInstance()); // Whenever Illuminator Virtuoso becomes the target of a spell you control, it connives. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new ConniveSourceEffect(), filter ).setTriggerPhrase("Whenever {this} becomes the target of a spell you control, ")); } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryServant.java b/Mage.Sets/src/mage/cards/i/IllusionaryServant.java index fa8b1863d36..817ac856377 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryServant.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryServant.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class IllusionaryServant extends CardImpl { this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private IllusionaryServant(final IllusionaryServant card) { diff --git a/Mage.Sets/src/mage/cards/l/LivewireLash.java b/Mage.Sets/src/mage/cards/l/LivewireLash.java index b432e3004df..a2ef4037517 100644 --- a/Mage.Sets/src/mage/cards/l/LivewireLash.java +++ b/Mage.Sets/src/mage/cards/l/LivewireLash.java @@ -1,7 +1,7 @@ package mage.cards.l; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DamageTargetEffect; @@ -30,7 +30,7 @@ public final class LivewireLash extends CardImpl { // Equipped creature gets +2/+0 and has "Whenever this creature becomes the target of a spell, this creature deals 2 damage to any target." Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); - Ability ability2 = new SourceBecomesTargetTriggeredAbility( + Ability ability2 = new BecomesTargetSourceTriggeredAbility( new DamageTargetEffect(2, "it"), StaticFilters.FILTER_SPELL_A ).setTriggerPhrase("Whenever this creature becomes the target of a spell, "); ability2.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java index 7d69a47fb48..d152ee8a24e 100644 --- a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java +++ b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java @@ -2,7 +2,7 @@ package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; @@ -116,7 +116,7 @@ class MakeshiftMannequinGainAbilityEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { permanent.addAbility( - new SourceBecomesTargetTriggeredAbility( + new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect()), source.getSourceId(), game); return true; diff --git a/Mage.Sets/src/mage/cards/m/Mirozel.java b/Mage.Sets/src/mage/cards/m/Mirozel.java index af1fd85e638..8eea6ec34e1 100644 --- a/Mage.Sets/src/mage/cards/m/Mirozel.java +++ b/Mage.Sets/src/mage/cards/m/Mirozel.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; @@ -28,7 +28,7 @@ public final class Mirozel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Mirozel becomes the target of a spell or ability, return Mirozel to its owner's hand. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new ReturnToHandSourceEffect(true))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ReturnToHandSourceEffect(true))); } private Mirozel(final Mirozel card) { diff --git a/Mage.Sets/src/mage/cards/m/MyriadConstruct.java b/Mage.Sets/src/mage/cards/m/MyriadConstruct.java index 1de37fe7492..116e49c98fa 100644 --- a/Mage.Sets/src/mage/cards/m/MyriadConstruct.java +++ b/Mage.Sets/src/mage/cards/m/MyriadConstruct.java @@ -2,7 +2,7 @@ package mage.cards.m; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.dynamicvalue.DynamicValue; @@ -60,7 +60,7 @@ public final class MyriadConstruct extends CardImpl { )); // When Myriad Construct becomes the target of a spell, sacrifice it and create a number of 1/1 colourless Construct artifact creature tokens equal to its power. - Ability ability = new SourceBecomesTargetTriggeredAbility( + Ability ability = new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A ); ability.addEffect(new CreateTokenEffect(new ConstructToken(), xValue2) diff --git a/Mage.Sets/src/mage/cards/o/OpalineSliver.java b/Mage.Sets/src/mage/cards/o/OpalineSliver.java index fd59e9c43e0..22dc99d327a 100644 --- a/Mage.Sets/src/mage/cards/o/OpalineSliver.java +++ b/Mage.Sets/src/mage/cards/o/OpalineSliver.java @@ -3,7 +3,7 @@ package mage.cards.o; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; @@ -34,7 +34,7 @@ public final class OpalineSliver extends CardImpl { this.toughness = new MageInt(2); // All Slivers have "Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card." - Ability gainedTriggeredAbility = new SourceBecomesTargetTriggeredAbility( + Ability gainedTriggeredAbility = new BecomesTargetSourceTriggeredAbility( new DrawCardSourceControllerEffect(1), filterSpell, SetTargetPointer.NONE, true) .setTriggerPhrase("Whenever this permanent becomes the target of a spell an opponent controls, "); this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( diff --git a/Mage.Sets/src/mage/cards/p/Petrahydrox.java b/Mage.Sets/src/mage/cards/p/Petrahydrox.java index 04f81ab1ddc..270d27a3ae2 100644 --- a/Mage.Sets/src/mage/cards/p/Petrahydrox.java +++ b/Mage.Sets/src/mage/cards/p/Petrahydrox.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Petrahydrox extends CardImpl { this.toughness = new MageInt(3); // When Petrahydrox becomes the target of a spell or ability, return Petrahydrox to its owner's hand. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new ReturnToHandSourceEffect(true))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ReturnToHandSourceEffect(true))); } private Petrahydrox(final Petrahydrox card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java b/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java index bf574189fac..ac11be77424 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class PhantasmalAbomination extends CardImpl { this.toughness = new MageInt(5); this.addAbility(DefenderAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private PhantasmalAbomination(final PhantasmalAbomination card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalBear.java b/Mage.Sets/src/mage/cards/p/PhantasmalBear.java index c3307300c86..7ad399cabb1 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalBear.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalBear.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class PhantasmalBear extends CardImpl { this.toughness = new MageInt(2); // When Phantasmal Bear becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantasmalBear(final PhantasmalBear card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java b/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java index 7c5033d5a40..f1fce0b8755 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class PhantasmalDragon extends CardImpl { this.toughness = new MageInt(5); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantasmalDragon(final PhantasmalDragon card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java b/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java index c2c6098bce7..7333117217f 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java @@ -1,7 +1,7 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class PhantasmalDreadmaw extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Phantasmal Dreadmaw becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private PhantasmalDreadmaw(final PhantasmalDreadmaw card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java index b8830a895a1..9466bd0b5c3 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java @@ -4,7 +4,7 @@ package mage.cards.p; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyPermanentEffect; @@ -31,7 +31,7 @@ public final class PhantasmalImage extends CardImpl { public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities blueprint.addSubType(SubType.ILLUSION); - blueprint.getAbilities().add(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + blueprint.getAbilities().add(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); return true; } }; diff --git a/Mage.Sets/src/mage/cards/p/PhantomBeast.java b/Mage.Sets/src/mage/cards/p/PhantomBeast.java index 3a9ffaeca91..0af4e336330 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomBeast.java +++ b/Mage.Sets/src/mage/cards/p/PhantomBeast.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class PhantomBeast extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantomBeast(final PhantomBeast card) { diff --git a/Mage.Sets/src/mage/cards/r/RealitySmasher.java b/Mage.Sets/src/mage/cards/r/RealitySmasher.java index 55c441ce5ff..3c0222f83dc 100644 --- a/Mage.Sets/src/mage/cards/r/RealitySmasher.java +++ b/Mage.Sets/src/mage/cards/r/RealitySmasher.java @@ -1,7 +1,7 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.HasteAbility; @@ -38,7 +38,7 @@ public final class RealitySmasher extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); // Whenever Reality Smasher becomes the target of a spell an opponent controls, counter that spell unless its controller discards a card. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new CounterUnlessPaysEffect(new DiscardCardCost()).setText("counter that spell unless its controller discards a card"), filter, SetTargetPointer.SPELL, false )); diff --git a/Mage.Sets/src/mage/cards/s/SegmentedWurm.java b/Mage.Sets/src/mage/cards/s/SegmentedWurm.java index aefd03ae1e3..a93ecb91070 100644 --- a/Mage.Sets/src/mage/cards/s/SegmentedWurm.java +++ b/Mage.Sets/src/mage/cards/s/SegmentedWurm.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SegmentedWurm extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(5); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()))); } private SegmentedWurm(final SegmentedWurm card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java b/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java index a73b894c18e..27803bb8e1a 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class SkulkingFugitive extends CardImpl { this.toughness = new MageInt(4); // When Skulking Fugitive becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private SkulkingFugitive(final SkulkingFugitive card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingGhost.java b/Mage.Sets/src/mage/cards/s/SkulkingGhost.java index aace840275c..8bbc780db9a 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingGhost.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingGhost.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class SkulkingGhost extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Skulking Ghost becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private SkulkingGhost(final SkulkingGhost card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingKnight.java b/Mage.Sets/src/mage/cards/s/SkulkingKnight.java index 01255bb9e96..6b4871f8829 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingKnight.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingKnight.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlankingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class SkulkingKnight extends CardImpl { // Flanking this.addAbility(new FlankingAbility()); // When Skulking Knight becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private SkulkingKnight(final SkulkingKnight card) { diff --git a/Mage.Sets/src/mage/cards/s/StormchaserDrake.java b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java index b5189a54619..c4c67739090 100644 --- a/Mage.Sets/src/mage/cards/s/StormchaserDrake.java +++ b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public final class StormchaserDrake extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Stormchaser Drake becomes the target of a spell you control, draw a card. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DrawCardSourceControllerEffect(1), filter ).setTriggerPhrase("Whenever {this} becomes the target of a spell you control, ")); } diff --git a/Mage.Sets/src/mage/cards/t/TarPitWarrior.java b/Mage.Sets/src/mage/cards/t/TarPitWarrior.java index becc6416ce1..0db257477bf 100644 --- a/Mage.Sets/src/mage/cards/t/TarPitWarrior.java +++ b/Mage.Sets/src/mage/cards/t/TarPitWarrior.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class TarPitWarrior extends CardImpl { this.toughness = new MageInt(4); // When Tar Pit Warrior becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private TarPitWarrior(final TarPitWarrior card) { diff --git a/Mage.Sets/src/mage/cards/t/TaskForce.java b/Mage.Sets/src/mage/cards/t/TaskForce.java index 694ee37a856..cf0ad997355 100644 --- a/Mage.Sets/src/mage/cards/t/TaskForce.java +++ b/Mage.Sets/src/mage/cards/t/TaskForce.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class TaskForce extends CardImpl { this.toughness = new MageInt(3); // Whenever Task Force becomes the target of a spell or ability, it gets +0/+3 until end of turn. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new BoostSourceEffect(0, 3, Duration.EndOfTurn, "it") ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability, ")); } diff --git a/Mage.Sets/src/mage/cards/t/TetheredSkirge.java b/Mage.Sets/src/mage/cards/t/TetheredSkirge.java index 87e9896d470..7eb2151f278 100644 --- a/Mage.Sets/src/mage/cards/t/TetheredSkirge.java +++ b/Mage.Sets/src/mage/cards/t/TetheredSkirge.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class TetheredSkirge extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Tethered Skirge becomes the target of a spell or ability, you lose 1 life. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new LoseLifeSourceControllerEffect(1))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeSourceControllerEffect(1))); } private TetheredSkirge(final TetheredSkirge card) { diff --git a/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java b/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java index 248a7d1e92e..a1c35af6ac6 100644 --- a/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java +++ b/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -45,7 +45,7 @@ public final class TheHowlingAbomination extends CardImpl { ))); // Electric Thunder—Whenever Blanka becomes the target of a spell, he gets +2/+2 until end of turn and deals 2 damage to each opponent. - Ability ability = new SourceBecomesTargetTriggeredAbility(new BoostSourceEffect( + Ability ability = new BecomesTargetSourceTriggeredAbility(new BoostSourceEffect( 2, 2, Duration.EndOfTurn ).setText("it gets +2/+2 until end of turn"), StaticFilters.FILTER_SPELL_A).setTriggerPhrase("Whenever {this} becomes the target of a spell, "); ability.addEffect(new DamagePlayersEffect(2, TargetController.OPPONENT) diff --git a/Mage.Sets/src/mage/cards/t/ThornLieutenant.java b/Mage.Sets/src/mage/cards/t/ThornLieutenant.java index 3e194e7b8b0..502440dd620 100644 --- a/Mage.Sets/src/mage/cards/t/ThornLieutenant.java +++ b/Mage.Sets/src/mage/cards/t/ThornLieutenant.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; @@ -30,7 +30,7 @@ public final class ThornLieutenant extends CardImpl { this.toughness = new MageInt(3); // Whenever Thorn Lieutenant becomes the target of a spell or ability an opponent controls, create a 1/1 green Elf Warrior creature token. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new CreateTokenEffect(new ElfWarriorToken()), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability an opponent controls, ")); diff --git a/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java b/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java index c065eb83421..6253c883f49 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java @@ -1,6 +1,6 @@ package mage.cards.t; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -29,7 +29,7 @@ public final class TreacherousBlessing extends CardImpl { )); // When Treacherous Blessing becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private TreacherousBlessing(final TreacherousBlessing card) { diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java index 6a4c068ce26..6bcdf000f94 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java @@ -1,7 +1,7 @@ package mage.cards.w; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class WardenOfTheWoods extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index a14ae4ba386..9eb1e3e49bb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -1,6 +1,6 @@ package org.mage.test.cards.copy; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.LifelinkAbility; import mage.constants.PhaseStep; @@ -629,7 +629,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertTrue("Phantasmal Image should not be a creature", !staffA.isCreature(currentGame)); assertTrue("Phantasmal Image should not be an Illusion", !staffA.hasSubtype(SubType.ILLUSION, currentGame)); assertTrue("Phantasmal Image should not be a Construct", !staffA.hasSubtype(SubType.CONSTRUCT, currentGame)); - assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(SourceBecomesTargetTriggeredAbility.class)); + assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(BecomesTargetSourceTriggeredAbility.class)); Permanent staffB = getPermanent("Chimeric Staff", playerB); assertTrue("Chimeric Staff should be an artifact", staffB.isArtifact(currentGame)); @@ -659,7 +659,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertTrue("Phantasmal Image should be a Rogue", cloakA.hasSubtype(SubType.ROGUE, currentGame)); assertTrue("Phantasmal Image should be an Illusion", cloakA.hasSubtype(SubType.ILLUSION, currentGame)); assertTrue("Phantasmal Image should be an Equipment", cloakA.hasSubtype(SubType.EQUIPMENT, currentGame)); - assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(SourceBecomesTargetTriggeredAbility.class)); + assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(BecomesTargetSourceTriggeredAbility.class)); Permanent cloakB = getPermanent("Cloak and Dagger", playerB); assertTrue("Cloak and Dagger should be an artifact", cloakB.isArtifact(currentGame)); diff --git a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java similarity index 86% rename from Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java rename to Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java index 6c2aa3d210a..51346890811 100644 --- a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java @@ -19,20 +19,20 @@ import mage.util.CardUtil; /** * @author North */ -public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { +public class BecomesTargetSourceTriggeredAbility extends TriggeredAbilityImpl { private final FilterStackObject filter; private final SetTargetPointer setTargetPointer; - public SourceBecomesTargetTriggeredAbility(Effect effect) { + public BecomesTargetSourceTriggeredAbility(Effect effect) { this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); } - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) { + public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter) { this(effect, filter, SetTargetPointer.NONE, false); } - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { + public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; this.setTargetPointer = setTargetPointer; @@ -43,15 +43,15 @@ public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { setTriggerPhrase((textWhen ? "When" : "Whenever") + " {this} becomes the target of " + filter.getMessage() + ", "); } - protected SourceBecomesTargetTriggeredAbility(final SourceBecomesTargetTriggeredAbility ability) { + protected BecomesTargetSourceTriggeredAbility(final BecomesTargetSourceTriggeredAbility ability) { super(ability); this.filter = ability.filter; this.setTargetPointer = ability.setTargetPointer; } @Override - public SourceBecomesTargetTriggeredAbility copy() { - return new SourceBecomesTargetTriggeredAbility(this); + public BecomesTargetSourceTriggeredAbility copy() { + return new BecomesTargetSourceTriggeredAbility(this); } @Override From 2a210404a28b35bcdb536b8cc07ac683259adab4 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 01:40:51 -0400 Subject: [PATCH 11/18] reimplement more cards to use BecomesTargetSource --- .../src/mage/cards/a/AshenmoorLiege.java | 59 ++--------------- .../src/mage/cards/e/EternalScourge.java | 46 ++----------- Mage.Sets/src/mage/cards/f/FblthpTheLost.java | 45 ++----------- .../src/mage/cards/f/ForsakenWastes.java | 66 ++++--------------- .../src/mage/cards/l/LabyrinthGuardian.java | 56 ++-------------- Mage.Sets/src/mage/cards/l/LavaRunner.java | 58 +++------------- Mage.Sets/src/mage/cards/m/Mossdog.java | 51 ++------------ Mage.Sets/src/mage/cards/r/Retromancer.java | 59 +++-------------- .../JaceCunningCastawayIllusionToken.java | 54 ++------------- 9 files changed, 66 insertions(+), 428 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java b/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java index 9671893a004..ca02268669b 100644 --- a/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java +++ b/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java @@ -1,25 +1,19 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -35,7 +29,6 @@ public final class AshenmoorLiege extends CardImpl { filterRedCreature.add(new ColorPredicate(ObjectColor.RED)); } - public AshenmoorLiege(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B/R}{B/R}{B/R}"); this.subtype.add(SubType.ELEMENTAL); @@ -50,10 +43,8 @@ public final class AshenmoorLiege extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterRedCreature, true))); // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. - this.addAbility(new AshenmoorLiegeTriggeredAbility()); - - - + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(4), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PLAYER, false)); } private AshenmoorLiege(final AshenmoorLiege card) { @@ -65,39 +56,3 @@ public final class AshenmoorLiege extends CardImpl { return new AshenmoorLiege(this); } } - -class AshenmoorLiegeTriggeredAbility extends TriggeredAbilityImpl { - - public AshenmoorLiegeTriggeredAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(4), false); - } - - private AshenmoorLiegeTriggeredAbility(final AshenmoorLiegeTriggeredAbility ability) { - super(ability); - } - - @Override - public AshenmoorLiegeTriggeredAbility copy() { - return new AshenmoorLiegeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, that player loses 4 life."; - } - -} diff --git a/Mage.Sets/src/mage/cards/e/EternalScourge.java b/Mage.Sets/src/mage/cards/e/EternalScourge.java index 390e7a38783..dab0e5cd815 100644 --- a/Mage.Sets/src/mage/cards/e/EternalScourge.java +++ b/Mage.Sets/src/mage/cards/e/EternalScourge.java @@ -1,10 +1,8 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileSourceEffect; @@ -12,9 +10,10 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; + +import java.util.UUID; /** * @@ -33,7 +32,8 @@ public final class EternalScourge extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.EXILED, new EternalScourgePlayEffect())); // When Eternal Scourge becomes the target of a spell or ability an opponent controls, exile Eternal Scourge. - this.addAbility(new EternalScourgeAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ExileSourceEffect(), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS)); } private EternalScourge(final EternalScourge card) { @@ -78,37 +78,3 @@ class EternalScourgePlayEffect extends AsThoughEffectImpl { return false; } } - -class EternalScourgeAbility extends TriggeredAbilityImpl { - - public EternalScourgeAbility() { - super(Zone.BATTLEFIELD, new ExileSourceEffect(), false); - } - - private EternalScourgeAbility(final EternalScourgeAbility ability) { - super(ability); - } - - @Override - public EternalScourgeAbility copy() { - return new EternalScourgeAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, exile {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java index eb393f990ca..8e667ab6b40 100644 --- a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java +++ b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java @@ -1,20 +1,19 @@ package mage.cards.f; import mage.MageInt; -import mage.MageObject; import mage.MageObjectReference; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; import java.util.HashSet; @@ -38,7 +37,8 @@ public final class FblthpTheLost extends CardImpl { this.addAbility(new FblthpTheLostTriggeredAbility()); // When Fblthp becomes the target of a spell, shuffle Fblthp into its owner's library. - this.addAbility(new FblthpTheLostTargetedTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), + StaticFilters.FILTER_SPELL_A)); } private FblthpTheLost(final FblthpTheLost card) { @@ -128,40 +128,3 @@ class FblthpTheLostWatcher extends Watcher { } } - -class FblthpTheLostTargetedTriggeredAbility extends TriggeredAbilityImpl { - - FblthpTheLostTargetedTriggeredAbility() { - super(Zone.BATTLEFIELD, new ShuffleIntoLibrarySourceEffect(), false); - } - - private FblthpTheLostTargetedTriggeredAbility(final FblthpTheLostTargetedTriggeredAbility ability) { - super(ability); - } - - @Override - public FblthpTheLostTargetedTriggeredAbility copy() { - return new FblthpTheLostTargetedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When {this} becomes the target of a spell, shuffle {this} into its owner's library."; - } - -} diff --git a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java index 82d7e4b19a0..4ce1a0fa725 100644 --- a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java +++ b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java @@ -1,28 +1,19 @@ - package mage.cards.f; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.CantGainLifeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.constants.*; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes + * @author xenohedron */ public final class ForsakenWastes extends CardImpl { @@ -31,13 +22,15 @@ public final class ForsakenWastes extends CardImpl { this.supertype.add(SuperType.WORLD); // Players can't gain life. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantGainLifeAllEffect())); + this.addAbility(new SimpleStaticAbility(new CantGainLifeAllEffect())); // At the beginning of each player's upkeep, that player loses 1 life. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), TargetController.ANY, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), + TargetController.ANY, false, true)); // Whenever Forsaken Wastes becomes the target of a spell, that spell's controller loses 5 life. - this.addAbility(new ForsakenWastesTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(5), + StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false)); } private ForsakenWastes(final ForsakenWastes card) { @@ -49,40 +42,3 @@ public final class ForsakenWastes extends CardImpl { return new ForsakenWastes(this); } } - -class ForsakenWastesTriggeredAbility extends TriggeredAbilityImpl { - - public ForsakenWastesTriggeredAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(5), false); - } - - private ForsakenWastesTriggeredAbility(final ForsakenWastesTriggeredAbility ability) { - super(ability); - } - - @Override - public ForsakenWastesTriggeredAbility copy() { - return new ForsakenWastesTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell, that spell's controller loses 5 life."; - } - -} diff --git a/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java b/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java index 805643e8e95..faf2adeb0fa 100644 --- a/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java +++ b/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java @@ -1,10 +1,7 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EmbalmAbility; @@ -12,16 +9,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes + * @author xenohedron */ public final class LabyrinthGuardian extends CardImpl { @@ -34,7 +27,7 @@ public final class LabyrinthGuardian extends CardImpl { this.toughness = new MageInt(3); // When Labyrinth Guardian becomes the target of a spell, sacrifice it. - this.addAbility(new LabyrinthGuardianTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A)); // Embalm {3}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl<>("{3}{U}"), this)); @@ -50,40 +43,3 @@ public final class LabyrinthGuardian extends CardImpl { return new LabyrinthGuardian(this); } } - -class LabyrinthGuardianTriggeredAbility extends TriggeredAbilityImpl { - - public LabyrinthGuardianTriggeredAbility() { - super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false); - } - - private LabyrinthGuardianTriggeredAbility(final LabyrinthGuardianTriggeredAbility ability) { - super(ability); - } - - @Override - public LabyrinthGuardianTriggeredAbility copy() { - return new LabyrinthGuardianTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When {this} becomes the target of a spell, sacrifice it."; - } - -} diff --git a/Mage.Sets/src/mage/cards/l/LavaRunner.java b/Mage.Sets/src/mage/cards/l/LavaRunner.java index d2e9aa10a49..ee1ae0b4812 100644 --- a/Mage.Sets/src/mage/cards/l/LavaRunner.java +++ b/Mage.Sets/src/mage/cards/l/LavaRunner.java @@ -1,26 +1,20 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author LoneFox - + * @author xenohedron */ public final class LavaRunner extends CardImpl { @@ -32,8 +26,11 @@ public final class LavaRunner extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); + // Whenever Lava Runner becomes the target of a spell or ability, that spell or ability's controller sacrifices a land. - this.addAbility(new LavaRunnerAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND_A, 1, "that spell or ability's controller"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false)); } private LavaRunner(final LavaRunner card) { @@ -45,38 +42,3 @@ public final class LavaRunner extends CardImpl { return new LavaRunner(this); } } - -class LavaRunnerAbility extends TriggeredAbilityImpl { - - public LavaRunnerAbility() { - super(Zone.BATTLEFIELD, new SacrificeEffect(new FilterLandPermanent(), 1, ""), false); - } - - private LavaRunnerAbility(final LavaRunnerAbility ability) { - super(ability); - } - - @Override - public LavaRunnerAbility copy() { - return new LavaRunnerAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if(event.getTargetId().equals(this.getSourceId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability, that spell or ability's controller sacrifices a land."; - } -} diff --git a/Mage.Sets/src/mage/cards/m/Mossdog.java b/Mage.Sets/src/mage/cards/m/Mossdog.java index 677884fbd61..a4613140e75 100644 --- a/Mage.Sets/src/mage/cards/m/Mossdog.java +++ b/Mage.Sets/src/mage/cards/m/Mossdog.java @@ -1,23 +1,19 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes & L_J + * @author xenohedron */ public final class Mossdog extends CardImpl { @@ -29,7 +25,8 @@ public final class Mossdog extends CardImpl { this.toughness = new MageInt(1); // Whenever Mossdog becomes the target of a spell or ability an opponent controls, put a +1/+1 counter on Mossdog. - this.addAbility(new MossdogAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS)); } private Mossdog(final Mossdog card) { @@ -41,37 +38,3 @@ public final class Mossdog extends CardImpl { return new Mossdog(this); } } - -class MossdogAbility extends TriggeredAbilityImpl { - - public MossdogAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - private MossdogAbility(final MossdogAbility ability) { - super(ability); - } - - @Override - public MossdogAbility copy() { - return new MossdogAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, put a +1/+1 counter on {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/r/Retromancer.java b/Mage.Sets/src/mage/cards/r/Retromancer.java index f7cc0d679ea..cff287af531 100644 --- a/Mage.Sets/src/mage/cards/r/Retromancer.java +++ b/Mage.Sets/src/mage/cards/r/Retromancer.java @@ -1,25 +1,19 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author L_J + * @author xenohedron */ public final class Retromancer extends CardImpl { @@ -31,7 +25,8 @@ public final class Retromancer extends CardImpl { this.toughness = new MageInt(3); // Whenever Retromancer becomes the target of a spell or ability, Retromancer deals 3 damage to that spell or ability's controller. - this.addAbility(new RetromancerTriggeredAbility(new DamageTargetEffect(3))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DamageTargetEffect(3), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false)); } private Retromancer(final Retromancer card) { @@ -43,41 +38,3 @@ public final class Retromancer extends CardImpl { return new Retromancer(this); } } - -class RetromancerTriggeredAbility extends TriggeredAbilityImpl { - - public RetromancerTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private RetromancerTriggeredAbility(final RetromancerTriggeredAbility ability) { - super(ability); - } - - @Override - public RetromancerTriggeredAbility copy() { - return new RetromancerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && event.getTargetId().equals(getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability, {this} deals 3 damage to that spell or ability's controller."; - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java b/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java index af042eb79d6..034e280d2d2 100644 --- a/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java @@ -1,16 +1,11 @@ - package mage.game.permanent.token; +import mage.MageInt; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.effects.common.SacrificeSourceEffect; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; +import mage.filter.StaticFilters; /** * @author TheElk801 @@ -26,10 +21,11 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl { power = new MageInt(2); toughness = new MageInt(2); - this.addAbility(new IllusionTokenTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A) + .setTriggerPhrase("When this creature becomes the target of a spell, ")); } - protected JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) { + private JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) { super(token); } @@ -38,39 +34,3 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl { return new JaceCunningCastawayIllusionToken(this); } } - -class IllusionTokenTriggeredAbility extends TriggeredAbilityImpl { - - public IllusionTokenTriggeredAbility() { - super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false); - } - - protected IllusionTokenTriggeredAbility(final IllusionTokenTriggeredAbility ability) { - super(ability); - } - - @Override - public IllusionTokenTriggeredAbility copy() { - return new IllusionTokenTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "When this creature becomes the target of a spell, sacrifice it."; - } - -} From 48d7d07f939650dde59aefa8425306a037269e03 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 23:46:09 -0400 Subject: [PATCH 12/18] reimplement Fractured Loyalty --- .../src/mage/cards/f/FracturedLoyalty.java | 132 +++++++----------- .../src/mage/cards/s/SpectralPrison.java | 7 +- ...BecomesTargetAttachedTriggeredAbility.java | 26 +++- 3 files changed, 73 insertions(+), 92 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java index 7e4cebd0d2f..2dc7d19d79a 100644 --- a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java +++ b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java @@ -2,7 +2,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; @@ -11,8 +11,8 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -37,7 +37,9 @@ public final class FracturedLoyalty extends CardImpl { this.addAbility(ability); // Whenever enchanted creature becomes the target of a spell or ability, that spell or ability's controller gains control of that creature. - this.addAbility(new FracturedLoyaltyTriggeredAbility()); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new FracturedLoyaltyEffect(), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false) + .setTriggerPhrase("Whenever enchanted creature becomes the target of a spell or ability, ")); } private FracturedLoyalty(final FracturedLoyalty card) { @@ -49,85 +51,47 @@ public final class FracturedLoyalty extends CardImpl { return new FracturedLoyalty(this); } - private static class FracturedLoyaltyEffect extends OneShotEffect { - - FracturedLoyaltyEffect() { - super(Outcome.GainControl); - this.staticText = "that spell or ability's controller gains control of that creature"; - } - - private FracturedLoyaltyEffect(final FracturedLoyaltyEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - // In the case that Fractured Loyalty is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - if (enchantment != null) { - Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); - if (enchantedCreature != null) { - Player controller = game.getPlayer(enchantedCreature.getControllerId()); - if (enchantment.getAttachedTo() != null) { - if (controller != null && !enchantedCreature.isControlledBy(this.getTargetPointer().getFirst(game, source))) { - ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, this.getTargetPointer().getFirst(game, source)); - effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); - game.addEffect(effect, source); - return true; - } - } - } - } - return false; - } - - @Override - public FracturedLoyaltyEffect copy() { - return new FracturedLoyaltyEffect(this); - } - - } - - class FracturedLoyaltyTriggeredAbility extends TriggeredAbilityImpl { - - public FracturedLoyaltyTriggeredAbility() { - super(Zone.BATTLEFIELD, new FracturedLoyaltyEffect(), false); - } - - private FracturedLoyaltyTriggeredAbility(final FracturedLoyaltyTriggeredAbility ability) { - super(ability); - } - - @Override - public FracturedLoyaltyTriggeredAbility copy() { - return new FracturedLoyaltyTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanentOrLKIBattlefield(this.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); - if (enchantedCreature != null && event.getTargetId().equals(enchantment.getAttachedTo())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature becomes the target of a spell or ability, that spell or ability's controller gains control of that creature."; - } - } +} + +class FracturedLoyaltyEffect extends OneShotEffect { + + FracturedLoyaltyEffect() { + super(Outcome.GainControl); + this.staticText = "that spell or ability's controller gains control of that creature"; + } + + private FracturedLoyaltyEffect(final FracturedLoyaltyEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + // In the case that Fractured Loyalty is blinked + Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + if (enchantment == null) { + // It was not blinked, use the standard method + enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + } + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); + if (enchantedCreature == null) { + return false; + } + Player controller = game.getPlayer(enchantedCreature.getControllerId()); + if (controller != null && !enchantedCreature.isControlledBy(this.getTargetPointer().getFirst(game, source))) { + ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, this.getTargetPointer().getFirst(game, source)); + effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); + game.addEffect(effect, source); + return true; + } + return false; + } + + @Override + public FracturedLoyaltyEffect copy() { + return new FracturedLoyaltyEffect(this); + } + } diff --git a/Mage.Sets/src/mage/cards/s/SpectralPrison.java b/Mage.Sets/src/mage/cards/s/SpectralPrison.java index 72b67c6d566..2268f6ac827 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralPrison.java +++ b/Mage.Sets/src/mage/cards/s/SpectralPrison.java @@ -9,10 +9,7 @@ import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -37,7 +34,7 @@ public final class SpectralPrison extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); // When enchanted creature becomes the target of a spell, sacrifice Spectral Prison. - this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A, false)); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, false)); } private SpectralPrison(final SpectralPrison card) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index 4b619a2ae7b..264942e928e 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -2,6 +2,7 @@ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterStackObject; import mage.filter.StaticFilters; @@ -9,6 +10,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -17,20 +19,23 @@ import mage.util.CardUtil; public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl { private final FilterStackObject filter; + private final SetTargetPointer setTargetPointer; public BecomesTargetAttachedTriggeredAbility(Effect effect) { - this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A, false); + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false); } - public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, boolean optional) { + public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; + this.setTargetPointer = setTargetPointer; setTriggerPhrase("When enchanted creature becomes the target of " + filter.getMessage() + ", "); } protected BecomesTargetAttachedTriggeredAbility(final BecomesTargetAttachedTriggeredAbility ability) { super(ability); this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; } @Override @@ -53,6 +58,21 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { return false; } - return CardUtil.checkTargetMap(this.id, targetingObject, event, game); + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); + break; + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAttachedTriggeredAbility"); + } + return true; } } From a7cda75b22a97665af6b699eaad71167a1b6ac89 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 23:24:45 -0400 Subject: [PATCH 13/18] new AuraSpellPredicate for Brine Comber, Fugitive Druid (related to #11174) --- Mage.Sets/src/mage/cards/b/BrineComber.java | 72 ++++--------------- .../src/mage/cards/b/BrineboundGift.java | 72 +++++-------------- Mage.Sets/src/mage/cards/f/FugitiveDruid.java | 5 +- .../predicate/other/AuraSpellPredicate.java | 31 ++++++++ 4 files changed, 65 insertions(+), 115 deletions(-) create mode 100644 Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java diff --git a/Mage.Sets/src/mage/cards/b/BrineComber.java b/Mage.Sets/src/mage/cards/b/BrineComber.java index b2d5b594d90..959136a2bd4 100644 --- a/Mage.Sets/src/mage/cards/b/BrineComber.java +++ b/Mage.Sets/src/mage/cards/b/BrineComber.java @@ -1,19 +1,19 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DisturbAbility; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; import mage.game.permanent.token.SpiritWhiteToken; -import mage.game.stack.Spell; import java.util.UUID; @@ -22,6 +22,12 @@ import java.util.UUID; */ public final class BrineComber extends CardImpl { + private static final FilterSpell filter = new FilterSpell("an Aura spell"); + + static { + filter.add(AuraSpellPredicate.instance); + } + public BrineComber(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); @@ -31,7 +37,10 @@ public final class BrineComber extends CardImpl { this.secondSideCardClazz = mage.cards.b.BrineboundGift.class; // Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - this.addAbility(new BrineComberTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken()), false, + "Whenever {this} enters the battlefield or becomes the target of an Aura spell, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, filter))); // Disturb {W}{U} this.addAbility(new DisturbAbility(this, "{W}{U}")); @@ -46,54 +55,3 @@ public final class BrineComber extends CardImpl { return new BrineComber(this); } } - -class BrineComberTriggeredAbility extends TriggeredAbilityImpl { - - BrineComberTriggeredAbility() { - super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); - } - - private BrineComberTriggeredAbility(final BrineComberTriggeredAbility effect) { - super(effect); - } - - @Override - public BrineComberTriggeredAbility copy() { - return new BrineComberTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - if (this.getSourcePermanentIfItStillExists(game) == null - || !event.getTargetId().equals(getSourceId())) { - return false; - } - Spell spell = game.getSpell(event.getSourceId()); - if(spell == null) { - return false; - } - SpellAbility spellAbility = (SpellAbility) spell.getStackAbility(); - return spellAbility != null - && spellAbility.getCharacteristics(game).hasSubtype(SubType.AURA, game); - } - - @Override - public String getRule() { - return "Whenever {this} enters the battlefield or becomes the target " + - "of an Aura spell, create a 1/1 white Spirit creature token with flying."; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrineboundGift.java b/Mage.Sets/src/mage/cards/b/BrineboundGift.java index 530f797aa62..7789d0a53b3 100644 --- a/Mage.Sets/src/mage/cards/b/BrineboundGift.java +++ b/Mage.Sets/src/mage/cards/b/BrineboundGift.java @@ -1,23 +1,20 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.*; +import mage.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; import mage.game.permanent.token.SpiritWhiteToken; -import mage.game.stack.Spell; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -28,6 +25,12 @@ import java.util.UUID; */ public final class BrineboundGift extends CardImpl { + private static final FilterSpell filter = new FilterSpell("an Aura spell"); + + static { + filter.add(AuraSpellPredicate.instance); + } + public BrineboundGift(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); @@ -44,7 +47,10 @@ public final class BrineboundGift extends CardImpl { this.addAbility(ability); // Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - this.addAbility(new BrineboundGiftTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken()), false, + "Whenever {this} enters the battlefield or enchanted creature becomes the target of an Aura spell, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetAttachedTriggeredAbility(null, filter, SetTargetPointer.NONE, false))); // If Brinebound Gift would be put into a graveyard from anywhere, exile it instead. this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); @@ -59,49 +65,3 @@ public final class BrineboundGift extends CardImpl { return new BrineboundGift(this); } } - -class BrineboundGiftTriggeredAbility extends TriggeredAbilityImpl { - - BrineboundGiftTriggeredAbility() { - super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); - } - - private BrineboundGiftTriggeredAbility(final BrineboundGiftTriggeredAbility effect) { - super(effect); - } - - @Override - public BrineboundGiftTriggeredAbility copy() { - return new BrineboundGiftTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - Permanent permanent = this.getSourcePermanentOrLKI(game); - if (permanent == null || !event.getTargetId().equals(permanent.getAttachedTo())) { - return false; - } - Spell spell = game.getSpell(event.getSourceId()); - return spell != null && spell.hasSubtype(SubType.AURA, game); - } - - @Override - public String getRule() { - return "Whenever {this} enters the battlefield or enchanted creature becomes the target " + - "of an Aura spell, create a 1/1 white Spirit creature token with flying."; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java index aaf01e3282c..0bd4df57998 100644 --- a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java +++ b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java @@ -10,6 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; /** * @@ -20,7 +21,7 @@ public final class FugitiveDruid extends CardImpl { private static final FilterSpell filter = new FilterSpell("an Aura spell"); static { - filter.add(SubType.AURA.getPredicate()); + filter.add(AuraSpellPredicate.instance); } public FugitiveDruid(UUID ownerId, CardSetInfo setInfo) { @@ -31,7 +32,7 @@ public final class FugitiveDruid extends CardImpl { this.toughness = new MageInt(2); // Whenever Fugitive Druid becomes the target of an Aura spell, you draw a card. - this.addAbility(new BecomesTargetSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), filter)); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DrawCardSourceControllerEffect(1, "you"), filter)); } private FugitiveDruid(final FugitiveDruid card) { diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java new file mode 100644 index 00000000000..dea183699c1 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java @@ -0,0 +1,31 @@ +package mage.filter.predicate.other; + +import mage.abilities.SpellAbility; +import mage.constants.SubType; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +/** + * Needed for "becomes the target of an Aura spell" to work correctly with e.g. Disturb, Bestow + * + * @author xenohedron + */ +public enum AuraSpellPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + if (!(input instanceof Spell)) { + return false; + } + SpellAbility spellAbility = ((Spell) input).getSpellAbility(); + return spellAbility != null && spellAbility.getCharacteristics(game).hasSubtype(SubType.AURA, game); + } + + @Override + public String toString() { + return "an Aura spell"; + } +} From 832a70a135106e0df007b177039e8a33ef439dbf Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 21 Sep 2023 23:53:11 -0400 Subject: [PATCH 14/18] reimplement remaining cards --- .../mage/cards/g/GargosViciousWatcher.java | 62 +++-------------- .../src/mage/cards/g/GoldspanDragon.java | 61 ++++------------- .../mage/cards/r/ReidaneGodOfTheWorthy.java | 63 +++-------------- .../src/mage/cards/s/ScalelordReckoner.java | 56 ++++++--------- Mage.Sets/src/mage/cards/s/ShieldMare.java | 54 +++------------ Mage.Sets/src/mage/cards/t/TectonicGiant.java | 66 +++++------------- Mage.Sets/src/mage/cards/w/Willbreaker.java | 68 ++++--------------- 7 files changed, 93 insertions(+), 337 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java index 0969dc07e4e..40e324de19b 100644 --- a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java +++ b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java @@ -1,8 +1,8 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -10,16 +10,12 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -48,9 +44,12 @@ public final class GargosViciousWatcher extends CardImpl { // Hydra spells you cast cost {4} less to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 4))); - // Whenever a creature you control becomes the target of a spell, Gargos, - // Vicious Watcher fights up to one target creature you don't control. - this.addAbility(new GargosViciousWatcherTriggeredAbility()); + // Whenever a creature you control becomes the target of a spell, Gargos, Vicious Watcher fights up to one target creature you don't control. + TriggeredAbility ability = new BecomesTargetAnyTriggeredAbility(new FightTargetSourceEffect(), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_A, + SetTargetPointer.NONE, false); + ability.addTarget(new TargetCreaturePermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); + this.addAbility(ability); } private GargosViciousWatcher(final GargosViciousWatcher card) { @@ -62,44 +61,3 @@ public final class GargosViciousWatcher extends CardImpl { return new GargosViciousWatcher(this); } } - -class GargosViciousWatcherTriggeredAbility extends TriggeredAbilityImpl { - - GargosViciousWatcherTriggeredAbility() { - super(Zone.BATTLEFIELD, new FightTargetSourceEffect()); - this.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); - } - - private GargosViciousWatcherTriggeredAbility(final GargosViciousWatcherTriggeredAbility ability) { - super(ability); - } - - @Override - public GargosViciousWatcherTriggeredAbility copy() { - return new GargosViciousWatcherTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - if (permanent == null - || object == null - || !permanent.isControlledBy(this.controllerId) - || !permanent.isCreature(game)) { - return false; - } - return object instanceof Spell; // must be a type of spell (instant, sorcery, or aura) - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of a spell, " - + "{this} fights up to one target creature you don't control."; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GoldspanDragon.java b/Mage.Sets/src/mage/cards/g/GoldspanDragon.java index 5347db525ac..ccd15cd7c68 100644 --- a/Mage.Sets/src/mage/cards/g/GoldspanDragon.java +++ b/Mage.Sets/src/mage/cards/g/GoldspanDragon.java @@ -1,9 +1,9 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeSourceCost; @@ -11,21 +11,21 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; -import mage.abilities.mana.SimpleManaAbility; -import mage.constants.Duration; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.filter.StaticFilters; import mage.game.permanent.token.TreasureToken; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; + +import java.util.UUID; /** * @@ -49,7 +49,10 @@ public final class GoldspanDragon extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever Goldspan Dragon attacks or becomes the target of a spell, create a Treasure token. - this.addAbility(new GoldspanDragonTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), false, + "Whenever {this} attacks or becomes the target of a spell, ", + new AttacksTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, StaticFilters.FILTER_SPELL_A))); // Treasures you control have "{T}, Sacrifice this artifact: Add two mana of any one color." Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); @@ -68,41 +71,3 @@ public final class GoldspanDragon extends CardImpl { return new GoldspanDragon(this); } } - -class GoldspanDragonTriggeredAbility extends TriggeredAbilityImpl { - - GoldspanDragonTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken())); - setTriggerPhrase("Whenever {this} attacks or becomes the target of a spell, "); - } - - private GoldspanDragonTriggeredAbility(final GoldspanDragonTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case DECLARED_ATTACKERS: - return game.getCombat().getAttackers().contains(this.getSourceId()); - case TARGETED: - if (event.getTargetId().equals(getSourceId())) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - return sourceObject instanceof Spell; - } - default: - return false; - } - } - - @Override - public GoldspanDragonTriggeredAbility copy() { - return new GoldspanDragonTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java index 89b364664a5..de5bd20a20c 100644 --- a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java @@ -3,7 +3,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.PreventionEffectImpl; @@ -17,12 +17,13 @@ import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacedCard; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.UUID; @@ -33,10 +34,12 @@ import java.util.UUID; public final class ReidaneGodOfTheWorthy extends ModalDoubleFacedCard { private static final FilterPermanent filter = new FilterLandPermanent("snow lands your opponents control"); + private static final FilterPermanent filterAnother = new FilterControlledPermanent("another permanent you control"); static { filter.add(SuperType.SNOW.getPredicate()); filter.add(TargetController.OPPONENT.getControllerPredicate()); + filterAnother.add(AnotherPredicate.instance); } public ReidaneGodOfTheWorthy(UUID ownerId, CardSetInfo setInfo) { @@ -70,8 +73,10 @@ public final class ReidaneGodOfTheWorthy extends ModalDoubleFacedCard { // If a source an opponent controls would deal damage to you or a permanent you control, prevent 1 of that damage. this.getRightHalfCard().addAbility(new SimpleStaticAbility(new ValkmiraProtectorsShieldPreventionEffect())); - // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. - this.getRightHalfCard().addAbility(new ValkmiraProtectorsShieldTriggeredAbility()); + // Whenever you or another permanent you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. + this.getRightHalfCard().addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)) + .setText("counter that spell or ability unless its controller pays {1}"), + filterAnother, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private ReidaneGodOfTheWorthy(final ReidaneGodOfTheWorthy card) { @@ -158,51 +163,3 @@ class ValkmiraProtectorsShieldPreventionEffect extends PreventionEffectImpl { return new ValkmiraProtectorsShieldPreventionEffect(this); } } - -class ValkmiraProtectorsShieldTriggeredAbility extends TriggeredAbilityImpl { - - ValkmiraProtectorsShieldTriggeredAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(1))); - } - - private ValkmiraProtectorsShieldTriggeredAbility(final ValkmiraProtectorsShieldTriggeredAbility ability) { - super(ability); - } - - @Override - public ValkmiraProtectorsShieldTriggeredAbility copy() { - return new ValkmiraProtectorsShieldTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(getSourceId())) { - return false; - } - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { - return false; - } - if (isControlledBy(event.getTargetId())) { - this.getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); - return true; - } - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null || !permanent.isControlledBy(getControllerId())) { - return false; - } - this.getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); - return true; - } - - @Override - public String getRule() { - return "Whenever you or another permanent you control becomes the target of a spell or ability " + - "an opponent controls, counter that spell or ability unless its controller pays {1}."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java b/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java index 5766da6cd85..3ff98c16e20 100644 --- a/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java +++ b/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java @@ -1,25 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** * * @author spjspj @@ -37,7 +36,7 @@ public final class ScalelordReckoner extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls. - this.addAbility(new ScalelardReckonerTriggeredAbility(new DestroyTargetEffect())); + this.addAbility(new ScalelordReckonerTriggeredAbility()); } private ScalelordReckoner(final ScalelordReckoner card) { @@ -50,49 +49,38 @@ public final class ScalelordReckoner extends CardImpl { } } -class ScalelardReckonerTriggeredAbility extends TriggeredAbilityImpl { +class ScalelordReckonerTriggeredAbility extends BecomesTargetAnyTriggeredAbility { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Dragon creature you control"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Dragon you control"); static { filter.add(SubType.DRAGON.getPredicate()); } - public ScalelardReckonerTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(), false); + ScalelordReckonerTriggeredAbility() { + super(new DestroyTargetEffect().setText("destroy target nonland permanent that player controls"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, false); } - private ScalelardReckonerTriggeredAbility(final ScalelardReckonerTriggeredAbility ability) { + private ScalelordReckonerTriggeredAbility(final ScalelordReckonerTriggeredAbility ability) { super(ability); } @Override - public ScalelardReckonerTriggeredAbility copy() { - return new ScalelardReckonerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; + public ScalelordReckonerTriggeredAbility copy() { + return new ScalelordReckonerTriggeredAbility(this); } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent that player controls"); - filter.add(new ControllerIdPredicate(event.getPlayerId())); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } + if (!super.checkTrigger(event, game)) { + return false; } - return false; + FilterNonlandPermanent targetFilter = new FilterNonlandPermanent("nonland permanent that player controls"); + targetFilter.add(new ControllerIdPredicate(event.getPlayerId())); + this.getTargets().clear(); + this.addTarget(new TargetPermanent(targetFilter)); + return true; } - @Override - public String getRule() { - return "Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls."; - } } diff --git a/Mage.Sets/src/mage/cards/s/ShieldMare.java b/Mage.Sets/src/mage/cards/s/ShieldMare.java index 393fad27de2..b7dfe5055ee 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldMare.java +++ b/Mage.Sets/src/mage/cards/s/ShieldMare.java @@ -2,20 +2,21 @@ package mage.cards.s; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import java.util.UUID; @@ -43,7 +44,10 @@ public final class ShieldMare extends CardImpl { )); // When Shield Mare enters the battlefield or becomes the target of a spell or ability and opponent controls, you gain 3 life. - this.addAbility(new ShieldMareTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new GainLifeEffect(3), false, + "When {this} enters the battlefield or becomes the target of a spell or ability an opponent controls, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS))); } private ShieldMare(final ShieldMare card) { @@ -55,45 +59,3 @@ public final class ShieldMare extends CardImpl { return new ShieldMare(this); } } - -class ShieldMareTriggeredAbility extends TriggeredAbilityImpl { - - public ShieldMareTriggeredAbility() { - super(Zone.ALL, new GainLifeEffect(3)); - } - - private ShieldMareTriggeredAbility(final ShieldMareTriggeredAbility effect) { - super(effect); - } - - @Override - public ShieldMareTriggeredAbility copy() { - return new ShieldMareTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - return event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.getControllerId()).contains(game.getControllerId(event.getSourceId())); - } - - @Override - public String getRule() { - return "When {this} enters the battlefield or becomes the target " - + "of a spell or ability an opponent controls, you gain 3 life"; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TectonicGiant.java b/Mage.Sets/src/mage/cards/t/TectonicGiant.java index f468bc1d025..3b521f8dc3e 100644 --- a/Mage.Sets/src/mage/cards/t/TectonicGiant.java +++ b/Mage.Sets/src/mage/cards/t/TectonicGiant.java @@ -1,31 +1,38 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.*; import mage.constants.*; +import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author TheElk801 */ public final class TectonicGiant extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public TectonicGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); @@ -37,7 +44,12 @@ public final class TectonicGiant extends CardImpl { // Whenever Tectonic Giant attacks or becomes the target of a spell an opponent controls, choose one — // • Tectonic Giant deals 3 damage to each opponent. // • Exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. - this.addAbility(new TectonicGiantTriggeredAbility()); + TriggeredAbility ability = new OrTriggeredAbility(Zone.BATTLEFIELD, new DamagePlayersEffect(3, TargetController.OPPONENT), false, + "Whenever {this} attacks or becomes the target of a spell an opponent controls, ", + new AttacksTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, filter)); + ability.addMode(new Mode(new TectonicGiantEffect())); + this.addAbility(ability); } private TectonicGiant(final TectonicGiant card) { @@ -50,48 +62,6 @@ public final class TectonicGiant extends CardImpl { } } -class TectonicGiantTriggeredAbility extends TriggeredAbilityImpl { - - TectonicGiantTriggeredAbility() { - super(Zone.BATTLEFIELD, new DamagePlayersEffect(3, TargetController.OPPONENT), false); - this.addMode(new Mode(new TectonicGiantEffect())); - setTriggerPhrase("Whenever {this} attacks or becomes the target of a spell an opponent controls, "); - } - - private TectonicGiantTriggeredAbility(final TectonicGiantTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case DECLARED_ATTACKERS: - return game.getCombat().getAttackers().contains(this.getSourceId()); - case TARGETED: - if (event.getTargetId().equals(getSourceId())) { - MageObject mageObject = game.getObject(event.getSourceId()); - Player player = game.getPlayer(getControllerId()); - return mageObject != null - && mageObject instanceof Spell - && player != null - && player.hasOpponent(((Spell) mageObject).getControllerId(), game); - } - } - return false; - } - - @Override - public TectonicGiantTriggeredAbility copy() { - return new TectonicGiantTriggeredAbility(this); - } -} - class TectonicGiantEffect extends OneShotEffect { TectonicGiantEffect() { diff --git a/Mage.Sets/src/mage/cards/w/Willbreaker.java b/Mage.Sets/src/mage/cards/w/Willbreaker.java index a8fda611681..48ddbfd422f 100644 --- a/Mage.Sets/src/mage/cards/w/Willbreaker.java +++ b/Mage.Sets/src/mage/cards/w/Willbreaker.java @@ -1,26 +1,26 @@ package mage.cards.w; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.constants.*; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; import java.util.UUID; /** - * @author LevelX2 + * @author xenohedron */ public final class Willbreaker extends CardImpl { + private static final FilterStackObject filter = new FilterStackObject("a spell or ability you control"); + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + public Willbreaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.HUMAN); @@ -29,7 +29,8 @@ public final class Willbreaker extends CardImpl { this.toughness = new MageInt(3); // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. - this.addAbility(new WillbreakerTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled), + StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE, filter, SetTargetPointer.PERMANENT, false)); } private Willbreaker(final Willbreaker card) { @@ -41,48 +42,3 @@ public final class Willbreaker extends CardImpl { return new Willbreaker(this); } } - -class WillbreakerTriggeredAbility extends TriggeredAbilityImpl { - - WillbreakerTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.WhileControlled)); - } - - private WillbreakerTriggeredAbility(final WillbreakerTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Permanent permanent = game.getPermanent(event.getTargetId()); - Permanent willbreaker = game.getPermanent(sourceId); - if (willbreaker == null // If you lose control of Willbreaker before its ability resolves, you won’t gain control of the creature at all. - || permanent == null - || !permanent.isCreature(game) - || !game.getOpponents(getControllerId()).contains(permanent.getControllerId())) { - return false; - } - // always call this method for FixedTargets in case it is blinked - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - - @Override - public String getRule() { - return "Whenever a creature an opponent controls becomes the target of a spell or ability you control, " - + "gain control of that creature for as long as you control {this}."; - } - - @Override - public WillbreakerTriggeredAbility copy() { - return new WillbreakerTriggeredAbility(this); - } -} From 79157a72192e799a389e9a189ce988144ac6ccbe Mon Sep 17 00:00:00 2001 From: xenohedron Date: Fri, 22 Sep 2023 18:52:52 -0400 Subject: [PATCH 15/18] fix Agrus Kos, Eternal Soldier --- .../mage/cards/a/AgrusKosEternalSoldier.java | 13 ++-- .../j22/AgrusKosEternalSoldierTest.java | 70 +++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java diff --git a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java index 994590006af..b79699c7004 100644 --- a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java +++ b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java @@ -19,9 +19,11 @@ import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; +import mage.util.CardUtil; import java.util.Collection; import java.util.List; @@ -88,11 +90,14 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (!event.getTargetId().equals(getSourceId())) { return false; } - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject == null) { + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || targetingObject instanceof Spell) { return false; } - Set targets = stackObject + if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + return false; + } + Set targets = targetingObject .getStackAbility() .getTargets() .stream() @@ -102,7 +107,7 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (targets.isEmpty() || !targets.stream().allMatch(getSourceId()::equals)) { return false; } - this.getEffects().setValue("triggeringAbility", stackObject); + this.getEffects().setValue("triggeringAbility", targetingObject); return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java new file mode 100644 index 00000000000..0df627d9194 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.single.j22; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class AgrusKosEternalSoldierTest extends CardTestPlayerBase { + + private static final String agrus = "Agrus Kos, Eternal Soldier"; // 3/4 Vigilance + // Whenever Agrus Kos, Eternal Soldier becomes the target of an ability that targets only it, you may pay {1}{R/W}. + // If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. + private static final String sparkmage = "Cunning Sparkmage"; // 0/1 Haste pinger + private static final String turtle = "Horned Turtle"; // 1/4 + private static final String firewalker = "Kor Firewalker"; // 2/2 protection from red + // Whenever a player casts a red spell, you may gain 1 life. + + @Test + public void testTriggerCopiesAbility() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, agrus); + addCard(Zone.BATTLEFIELD, playerA, turtle); + addCard(Zone.BATTLEFIELD, playerA, firewalker); + addCard(Zone.BATTLEFIELD, playerB, sparkmage); + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: {this} deals", agrus); + setChoice(playerA, true); // pay to copy + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTapped(sparkmage, true); + assertTappedCount("Plateau", true, 2); + assertDamageReceived(playerA, agrus, 1); + assertDamageReceived(playerA, turtle, 1); + assertDamageReceived(playerA, firewalker, 0); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testTriggerDoesntCopySpell() { + setStrictChooseMode(true); + + String shock = "Shock"; + addCard(Zone.BATTLEFIELD, playerA, agrus); + addCard(Zone.BATTLEFIELD, playerA, turtle); + addCard(Zone.BATTLEFIELD, playerA, firewalker); + addCard(Zone.HAND, playerB, shock); + addCard(Zone.BATTLEFIELD, playerB, "Plateau"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, shock, agrus); + setChoice(playerA, true); // gain 1 life + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerA, agrus, 2); + assertDamageReceived(playerA, turtle, 0); + assertDamageReceived(playerA, firewalker, 0); + assertLife(playerA, 21); + assertLife(playerB, 20); + } + +} From cd5ee1c31d4e74adac206bc6e1d268764bb4696c Mon Sep 17 00:00:00 2001 From: xenohedron Date: Fri, 22 Sep 2023 23:28:07 -0400 Subject: [PATCH 16/18] text fixes --- Mage.Sets/src/mage/cards/c/CloudCover.java | 2 +- .../src/mage/cards/d/DiffusionSliver.java | 3 +- Mage.Sets/src/mage/cards/f/FblthpTheLost.java | 3 +- .../src/mage/cards/f/ForsakenWastes.java | 2 +- Mage.Sets/src/mage/cards/r/Retromancer.java | 3 +- .../src/mage/cards/t/ThunderbreakRegent.java | 2 +- Mage.Sets/src/mage/cards/w/WildDefiance.java | 3 +- .../common/ReturnToHandTargetEffect.java | 4 +-- .../continuous/GainControlTargetEffect.java | 29 ++----------------- 9 files changed, 15 insertions(+), 36 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CloudCover.java b/Mage.Sets/src/mage/cards/c/CloudCover.java index e6f80ec18ae..eb0670d0de6 100644 --- a/Mage.Sets/src/mage/cards/c/CloudCover.java +++ b/Mage.Sets/src/mage/cards/c/CloudCover.java @@ -28,7 +28,7 @@ public final class CloudCover extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{U}"); // Whenever another permanent you control becomes the target of a spell or ability an opponent controls, you may return that permanent to its owner's hand. - this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect(), + this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect().setText("return that permanent to its owner's hand"), filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PERMANENT, true)); } diff --git a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java index e4451da52a3..5a50eff6738 100644 --- a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java +++ b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java @@ -30,7 +30,8 @@ public final class DiffusionSliver extends CardImpl { this.toughness = new MageInt(1); // Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}. - this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)), + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)) + .setText("counter that spell or ability unless its controller pays {2}"), filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } diff --git a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java index 8e667ab6b40..95023f3fe20 100644 --- a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java +++ b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java @@ -37,7 +37,8 @@ public final class FblthpTheLost extends CardImpl { this.addAbility(new FblthpTheLostTriggeredAbility()); // When Fblthp becomes the target of a spell, shuffle Fblthp into its owner's library. - this.addAbility(new BecomesTargetSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new ShuffleIntoLibrarySourceEffect().setText("shuffle {this} into its owner's library"), StaticFilters.FILTER_SPELL_A)); } diff --git a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java index 4ce1a0fa725..2f03c32a4e4 100644 --- a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java +++ b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java @@ -29,7 +29,7 @@ public final class ForsakenWastes extends CardImpl { TargetController.ANY, false, true)); // Whenever Forsaken Wastes becomes the target of a spell, that spell's controller loses 5 life. - this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(5), + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(5).setText("that spell's controller loses 5 life"), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false)); } diff --git a/Mage.Sets/src/mage/cards/r/Retromancer.java b/Mage.Sets/src/mage/cards/r/Retromancer.java index cff287af531..7c0d366276f 100644 --- a/Mage.Sets/src/mage/cards/r/Retromancer.java +++ b/Mage.Sets/src/mage/cards/r/Retromancer.java @@ -25,7 +25,8 @@ public final class Retromancer extends CardImpl { this.toughness = new MageInt(3); // Whenever Retromancer becomes the target of a spell or ability, Retromancer deals 3 damage to that spell or ability's controller. - this.addAbility(new BecomesTargetSourceTriggeredAbility(new DamageTargetEffect(3), + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DamageTargetEffect(3) + .setText("{this} deals 3 damage to that spell or ability's controller"), StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false)); } diff --git a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java index 54a2d11e2ea..23707ccf010 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java +++ b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java @@ -34,7 +34,7 @@ public final class ThunderbreakRegent extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a Dragon you control becomes the target of a spell or ability your opponent controls, Thunderbreak Regent deals 3 damage to that player. - this.addAbility(new BecomesTargetAnyTriggeredAbility(new DamageTargetEffect(3), + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DamageTargetEffect(3).setText("{this} deals 3 damage to that player"), filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PLAYER, false)); } diff --git a/Mage.Sets/src/mage/cards/w/WildDefiance.java b/Mage.Sets/src/mage/cards/w/WildDefiance.java index 902a70a70e6..73f3c2a3605 100644 --- a/Mage.Sets/src/mage/cards/w/WildDefiance.java +++ b/Mage.Sets/src/mage/cards/w/WildDefiance.java @@ -19,7 +19,8 @@ public final class WildDefiance extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn. - this.addAbility(new BecomesTargetAnyTriggeredAbility(new BoostTargetEffect(3, 3, Duration.EndOfTurn), + this.addAbility(new BecomesTargetAnyTriggeredAbility( + new BoostTargetEffect(3, 3, Duration.EndOfTurn).setText("that creature gets +3/+3 until end of turn"), StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY)); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java index 255a65d35a7..a6cd2c97f41 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java @@ -10,8 +10,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.Target; -import mage.util.CardUtil; import java.util.*; @@ -63,7 +61,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "return " + getTargetPointer().describeTargets(mode.getTargets(), "") + + return "return " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") + (getTargetPointer().isPlural(mode.getTargets()) ? " to their owners' hands" : " to its owner's hand"); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java index 9e41a785f96..3549d825d8c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java @@ -12,8 +12,6 @@ import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; -import mage.util.CardUtil; import java.util.UUID; @@ -128,31 +126,10 @@ public class GainControlTargetEffect extends ContinuousEffectImpl { @Override public String getText(Mode mode) { - if (!staticText.isEmpty()) { + if (staticText != null && !staticText.isEmpty()) { return staticText; } - - if (mode.getTargets().isEmpty()) { - return "gain control of target permanent"; - } - - Target target = mode.getTargets().get(0); - StringBuilder sb = new StringBuilder("gain control of "); - if (target.getMaxNumberOfTargets() > 1) { - if (target.getMinNumberOfTargets() == 0) { - sb.append("up to "); - } - sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target "); - } else if (!target.getTargetName().startsWith("another")) { - if (target.getMinNumberOfTargets() == 0) { - sb.append("up to one "); - } - sb.append("target "); - } - sb.append(mode.getTargets().get(0).getTargetName()); - if (!duration.toString().isEmpty() && duration != Duration.EndOfGame) { - sb.append(' ').append(duration.toString()); - } - return sb.toString(); + return "gain control of " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") + + (duration.toString().isEmpty() || duration == Duration.EndOfGame ? "" : " " + duration.toString()); } } From 1b9bcb92e6570415ce94e0b92ef04a098219db38 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 24 Sep 2023 00:22:21 -0400 Subject: [PATCH 17/18] improve targetMap usage, naming, docs per review --- .../mage/cards/a/AgrusKosEternalSoldier.java | 2 +- .../BecomesTargetAnyTriggeredAbility.java | 2 +- ...BecomesTargetAttachedTriggeredAbility.java | 2 +- ...comesTargetControllerTriggeredAbility.java | 2 +- .../BecomesTargetSourceTriggeredAbility.java | 2 +- .../mage/abilities/keyword/WardAbility.java | 2 +- Mage/src/main/java/mage/util/CardUtil.java | 31 +++++++++++-------- ...rOfTimesPermanentTargetedATurnWatcher.java | 2 +- 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java index b79699c7004..282cb292d6b 100644 --- a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java +++ b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java @@ -94,7 +94,7 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (targetingObject == null || targetingObject instanceof Spell) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } Set targets = targetingObject diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java index 0a4917cf4f4..e65f68dd59a 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java @@ -74,7 +74,7 @@ public class BecomesTargetAnyTriggeredAbility extends TriggeredAbilityImpl { if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index 264942e928e..b4098303ae0 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -58,7 +58,7 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java index b3eb9434c0a..ccb9a6dde2f 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java @@ -67,7 +67,7 @@ public class BecomesTargetControllerTriggeredAbility extends TriggeredAbilityImp if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java index 51346890811..3e51aa708da 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java @@ -68,7 +68,7 @@ public class BecomesTargetSourceTriggeredAbility extends TriggeredAbilityImpl { if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } switch (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java index c80fe36ee9a..604828361f2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -81,7 +81,7 @@ public class WardAbility extends TriggeredAbilityImpl { if (targetingObject == null || !game.getOpponents(getControllerId()).contains(targetingObject.getControllerId())) { return false; } - if (!CardUtil.checkTargetMap(this.id, targetingObject, event, game)) { + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { return false; } getEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index d20a4e8c9ee..c5b827b9c3b 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1012,8 +1012,8 @@ public final class CardUtil { /** * For finding the spell or ability on the stack for "becomes the target" triggers. - * @param event the GameEvent.EventType.TARGETED from checkTrigger() - * @param game the Game from checkTrigger() + * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() + * @param game the Game from checkTrigger() or watch() * @return the StackObject which targeted the source, or null if not found */ public static StackObject getTargetingStackObject(GameEvent event, Game game) { @@ -1035,31 +1035,36 @@ public final class CardUtil { } /** - * For ensuring that spells/abilities that target the same object twice only trigger "becomes the target" abilities once - * @param triggerId this.id of the TriggeredAbility + * For ensuring that spells/abilities that target the same object twice only trigger each "becomes the target" ability once. + * If this is the first attempt at triggering for a given ability targeting a given object, + * this method records that in the game state for later checks by this same method. + * @param checkingReference must be unique for each usage (this.id.toString() of the TriggeredAbility, or this.getKey() of the watcher) * @param targetingObject from getTargetingStackObject - * @param event the GameEvent.EventType.TARGETED from checkTrigger() - * @param game the Game from checkTrigger() - * @return false if already triggered, true if this is the first/only trigger + * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() + * @param game the Game from checkTrigger() or watch() + * @return true if already triggered/watched, false if this is the first/only trigger/watch */ - public static boolean checkTargetMap(UUID triggerId, StackObject targetingObject, GameEvent event, Game game) { + public static boolean checkTargetedEventAlreadyUsed(String checkingReference, StackObject targetingObject, GameEvent event, Game game) { + String stateKey = "targetedMap" + checkingReference; // If a spell or ability an opponent controls targets a single permanent you control more than once, // Battle Mammoth's triggered ability will trigger only once. // However, if a spell or ability an opponent controls targets multiple permanents you control, // Battle Mammoth's triggered ability will trigger once for each of those permanents. (2021-02-05) - Map> targetMap = (Map>) game.getState().getValue("targetMap" + triggerId); + Map> targetMap = (Map>) game.getState().getValue(stateKey); // targetMap: key - targetId; value - Set of stackObject Ids if (targetMap == null) { targetMap = new HashMap<>(); + } else { + targetMap = new HashMap<>(targetMap); // must have new object reference if saved back to game state } Set targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>()); if (!targetingObjects.add(targetingObject.getId())) { - return false; // The triggered ability already recorded that target of the stack object + return true; // The trigger/watcher already recorded that target of the stack object } - // Otherwise, store this combination of triggered ability / target / stack object + // Otherwise, store this combination of trigger/watcher + target + stack object targetMap.put(event.getTargetId(), targetingObjects); - game.getState().setValue("targetMap" + triggerId, targetMap); - return true; + game.getState().setValue(stateKey, targetMap); + return false; } /** diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index 55e1b0419e0..ec4149a0ce7 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -30,7 +30,7 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { return; } StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); - if (targetingObject == null || !CardUtil.checkTargetMap(null, targetingObject, event, game)) { + if (targetingObject == null || CardUtil.checkTargetedEventAlreadyUsed(this.getKey(), targetingObject, event, game)) { return; } Permanent permanent = game.getPermanent(event.getTargetId()); From 0287efcffb17727db60033742ac85ba324fb49d9 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 24 Sep 2023 00:52:38 -0400 Subject: [PATCH 18/18] minor cleanup --- .../common/NumberOfTimesPermanentTargetedATurnWatcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index ec4149a0ce7..c97cf9436f3 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -36,13 +36,13 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { MageObjectReference mor = new MageObjectReference(permanent, game); - int nTimes = permanentsTargeted.computeIfAbsent(mor, k -> 0); + int nTimes = permanentsTargeted.getOrDefault(mor, 0); permanentsTargeted.put(mor, nTimes + 1); } } public int numTimesTargetedThisTurn(Permanent permanent, Game game) { - return permanentsTargeted.computeIfAbsent(new MageObjectReference(permanent, game), k -> 0); + return permanentsTargeted.getOrDefault(new MageObjectReference(permanent, game), 0); } @Override