From eb0ae552570791cc391d567339b27a95e187b3da Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 24 Dec 2015 17:58:04 +0100 Subject: [PATCH] * Fixed a problem with "put into the graveyard from anywhere triggers" that checked wrongly the state of the source object on the battlefield if went to graveyard itself. --- .../sets/alarareborn/DragonAppeasement.java | 10 +- .../SmotheringAbomination.java | 1 + .../MazirekKraulDeathPriest.java | 6 +- .../dragonsoftarkir/RuthlessDeathfang.java | 3 +- .../sets/ravnica/SavraQueenOfTheGolgari.java | 9 +- .../triggers/UlamogTheInfiniteGyreTest.java | 31 ++++++ .../cards/triggers/dies/ThragtuskTest.java | 63 ++++++----- .../mage/abilities/TriggeredAbilities.java | 10 +- .../java/mage/abilities/TriggeredAbility.java | 4 + .../mage/abilities/TriggeredAbilityImpl.java | 45 +++++++- ...ThisOrAnotherCreatureTriggeredAbility.java | 4 +- ...sTheBattlefieldSourceTriggeredAbility.java | 4 +- ...aveFromAnywhereSourceTriggeredAbility.java | 28 +++++ ...aveFromBattlefieldAllTriggeredAbility.java | 10 +- .../common/ZoneChangeTriggeredAbility.java | 25 +++-- Mage/src/main/java/mage/counters/Counter.java | 104 +++++++++--------- 16 files changed, 229 insertions(+), 128 deletions(-) diff --git a/Mage.Sets/src/mage/sets/alarareborn/DragonAppeasement.java b/Mage.Sets/src/mage/sets/alarareborn/DragonAppeasement.java index 5ee8ca52d96..26e85928fd6 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/DragonAppeasement.java +++ b/Mage.Sets/src/mage/sets/alarareborn/DragonAppeasement.java @@ -50,16 +50,12 @@ public class DragonAppeasement extends CardImpl { super(ownerId, 115, "Dragon Appeasement", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{R}{G}"); this.expansionSetCode = "ARB"; - - - - // Skip your draw step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SkipDrawStepEffect())); - + // Whenever you sacrifice a creature, you may draw a card. this.addAbility(new DragonAppeasementTriggeredAbility()); - + } public DragonAppeasement(final DragonAppeasement card) { @@ -76,6 +72,7 @@ class DragonAppeasementTriggeredAbility extends TriggeredAbilityImpl { public DragonAppeasementTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); + setLeavesTheBattlefieldTrigger(true); } public DragonAppeasementTriggeredAbility(final DragonAppeasementTriggeredAbility ability) { @@ -103,4 +100,3 @@ class DragonAppeasementTriggeredAbility extends TriggeredAbilityImpl { return "Whenever you sacrifice a creature, " + super.getRule(); } } - diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/SmotheringAbomination.java b/Mage.Sets/src/mage/sets/battleforzendikar/SmotheringAbomination.java index e7d2ec93608..2f7aa19073f 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/SmotheringAbomination.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/SmotheringAbomination.java @@ -86,6 +86,7 @@ class SmotheringAbominationTriggeredAbility extends TriggeredAbilityImpl { public SmotheringAbominationTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + setLeavesTheBattlefieldTrigger(true); } public SmotheringAbominationTriggeredAbility(final SmotheringAbominationTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/sets/commander2015/MazirekKraulDeathPriest.java b/Mage.Sets/src/mage/sets/commander2015/MazirekKraulDeathPriest.java index 522f1c383f1..4ebdc8f15fa 100644 --- a/Mage.Sets/src/mage/sets/commander2015/MazirekKraulDeathPriest.java +++ b/Mage.Sets/src/mage/sets/commander2015/MazirekKraulDeathPriest.java @@ -61,7 +61,7 @@ public class MazirekKraulDeathPriest extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Whenever a player sacrifices another permanent, put a +1/+1 counter on each creature you control. this.addAbility(new PlayerSacrificesPermanentTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), new FilterControlledCreaturePermanent()), false)); } @@ -99,11 +99,11 @@ class PlayerSacrificesPermanentTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a player sacrifices a permanent, " + super.getRule(); + return "Whenever a player sacrifices another permanent, " + super.getRule(); } @Override public PlayerSacrificesPermanentTriggeredAbility copy() { return new PlayerSacrificesPermanentTriggeredAbility(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/RuthlessDeathfang.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/RuthlessDeathfang.java index b3385304d2b..d4d0f26f2b1 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/RuthlessDeathfang.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/RuthlessDeathfang.java @@ -79,6 +79,7 @@ class RuthlessDeathfangTriggeredAbility extends TriggeredAbilityImpl { public RuthlessDeathfangTriggeredAbility() { super(Zone.BATTLEFIELD, new SacrificeEffect(new FilterCreaturePermanent("creature"), 1, "target opponent"), false); + setLeavesTheBattlefieldTrigger(true); } public RuthlessDeathfangTriggeredAbility(final RuthlessDeathfangTriggeredAbility ability) { @@ -105,4 +106,4 @@ class RuthlessDeathfangTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever you sacrifice a creature, " + super.getRule(); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/ravnica/SavraQueenOfTheGolgari.java b/Mage.Sets/src/mage/sets/ravnica/SavraQueenOfTheGolgari.java index a3521d9cbdc..f007a3ce821 100644 --- a/Mage.Sets/src/mage/sets/ravnica/SavraQueenOfTheGolgari.java +++ b/Mage.Sets/src/mage/sets/ravnica/SavraQueenOfTheGolgari.java @@ -67,7 +67,7 @@ public class SavraQueenOfTheGolgari extends CardImpl { // Whenever you sacrifice a black creature, you may pay 2 life. If you do, each other player sacrifices a creature. this.addAbility(new SavraSacrificeBlackCreatureAbility()); - + // Whenever you sacrifice a green creature, you may gain 2 life. this.addAbility(new SavraSacrificeGreenCreatureAbility()); } @@ -87,6 +87,7 @@ class SavraSacrificeBlackCreatureAbility extends TriggeredAbilityImpl { public SavraSacrificeBlackCreatureAbility() { super(Zone.BATTLEFIELD, new DoIfCostPaid(new SavraSacrificeEffect(), new PayLifeCost(2))); this.addTarget(new TargetCreatureOrPlayer()); + this.setLeavesTheBattlefieldTrigger(true); } public SavraSacrificeBlackCreatureAbility(final SavraSacrificeBlackCreatureAbility ability) { @@ -136,7 +137,7 @@ class SavraSacrificeEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List perms = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + if (controller != null) { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null && !playerId.equals(source.getControllerId())) { @@ -154,9 +155,9 @@ class SavraSacrificeEffect extends OneShotEffect { permanent.sacrifice(source.getSourceId(), game); } } - return true; + return true; } - return false; + return false; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/UlamogTheInfiniteGyreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/UlamogTheInfiniteGyreTest.java index dfde867688b..ffcfa3fce88 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/UlamogTheInfiniteGyreTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/UlamogTheInfiniteGyreTest.java @@ -110,4 +110,35 @@ public class UlamogTheInfiniteGyreTest extends CardTestPlayerBase { assertHandCount(playerA, 4); } + + /** + * Ixidron turned Ulamog, the Infinite Gyre to a 2/2 with no name and then + * someone used an Wrath of the Gods effect. Ulamog, the Infinite Gyre + * entered the graveyard without triggering his shuffle effect (just like + * this case: http://www.mtgsalvation.com/forums/magi ... nd-kozilek). + */ + @Test + public void testFaceDownToGraveyard() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // When you cast Kozilek, Butcher of Truth, draw four cards. + // Annihilator 4 (Whenever this creature attacks, defending player sacrifices four permanents.) + // When Kozilek is put into a graveyard from anywhere, its owner shuffles his or her graveyard into his or her library. + addCard(Zone.BATTLEFIELD, playerA, "Kozilek, Butcher of Truth"); // {10} + // As Ixidron enters the battlefield, turn all other nontoken creatures face down. + // Ixidron's power and toughness are each equal to the number of face-down creatures on the battlefield. + addCard(Zone.HAND, playerA, "Ixidron"); // {3}{U}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + addCard(Zone.HAND, playerB, "Wrath of God", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ixidron"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Wrath of God"); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Ixidron", 0); + assertGraveyardCount(playerB, "Wrath of God", 1); + assertGraveyardCount(playerA, 0); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java index 3a9bb20f249..f2431830a1c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java @@ -29,24 +29,23 @@ package org.mage.test.cards.triggers.dies; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * Thragtusk - Beast {4}{G} - * When Thragtusk enters the battlefield, you gain 5 life. - * When Thragtusk leaves the battlefield, put a 3/3 green Beast creature token onto the battlefield. - * + * Thragtusk - Beast {4}{G} When Thragtusk enters the battlefield, you gain 5 + * life. When Thragtusk leaves the battlefield, put a 3/3 green Beast creature + * token onto the battlefield. + * * @author LevelX2 */ public class ThragtuskTest extends CardTestPlayerBase { /** - * Test if a Thragtusk is copied by a PhyrexianMetamorph - * that both triggers cotrrect work + * Test if a Thragtusk is copied by a PhyrexianMetamorph that both triggers + * cotrrect work */ - @Test + @Test public void testPhyrexianMetamorph() { addCard(Zone.BATTLEFIELD, playerA, "Island", 4); // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types @@ -57,9 +56,9 @@ public class ThragtuskTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Thragtusk", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph"); - setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); setChoice(playerA, "Thragtusk"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Public Execution", "Thragtusk"); setStopAt(1, PhaseStep.END_TURN); @@ -67,21 +66,22 @@ public class ThragtuskTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Thragtusk", 1); - assertGraveyardCount(playerA,"Phyrexian Metamorph", 1); - assertGraveyardCount(playerB,"Public Execution", 1); + assertGraveyardCount(playerA, "Phyrexian Metamorph", 1); + assertGraveyardCount(playerB, "Public Execution", 1); assertLife(playerA, 25); - assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start - - assertPermanentCount(playerA, "Beast", 1); + assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start + + assertPermanentCount(playerA, "Beast", 1); } + /** - * Test if a Thragtusk is copied by a Phyrexian Metamorph - * that leave battlefield ability does not work, if - * the copy left all abilities by Turn to Frog + * Test if a Thragtusk is copied by a Phyrexian Metamorph that leave + * battlefield ability does not work, if the copy left all abilities by Turn + * to Frog */ - + @Test public void testPhyrexianMetamorphTurnToFrog() { addCard(Zone.BATTLEFIELD, playerA, "Island", 4); @@ -96,32 +96,31 @@ public class ThragtuskTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Thragtusk", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph"); - setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); setChoice(playerA, "Thragtusk"); castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Tortoise Formation"); - - + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerB, "Turn to Frog", "Thragtusk"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Public Execution", "Thragtusk"); setStopAt(1, PhaseStep.END_TURN); execute(); - assertGraveyardCount(playerB,"Tortoise Formation", 1); - assertGraveyardCount(playerB,"Turn to Frog", 1); - + assertGraveyardCount(playerB, "Tortoise Formation", 1); + assertGraveyardCount(playerB, "Turn to Frog", 1); + assertPermanentCount(playerB, "Thragtusk", 1); assertPermanentCount(playerA, "Thragtusk", 0); - assertGraveyardCount(playerA,"Phyrexian Metamorph", 1); - assertGraveyardCount(playerB,"Public Execution", 1); + assertGraveyardCount(playerA, "Phyrexian Metamorph", 1); + assertGraveyardCount(playerB, "Public Execution", 1); assertLife(playerA, 25); - assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start - - assertPermanentCount(playerA, "Beast", 0); + assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start - } + assertPermanentCount(playerA, "Beast", 0); -} \ No newline at end of file + } + +} diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 0ea57904eaa..c066f8ef090 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -41,8 +41,6 @@ import mage.cards.Card; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -97,9 +95,11 @@ public class TriggeredAbilities extends ConcurrentHashMap 0) { count--; } else { - logger.warn("An attempt was made to set the counter '" + name + - "' to less than 0. Setting to 0."); + logger.warn("An attempt was made to set the counter '" + name + + "' to less than 0. Setting to 0."); } } - /** - * Decreases the {@code count} by tne passed in {@code amount}. Will not allow the count - * to be less than 0. If an attempt is made to make the count be less than zero, the call will be logged. + * Decreases the {@code count} by the passed in {@code amount}. Will not + * allow the count to be less than 0. If an attempt is made to make the + * count be less than zero, the call will be logged. + * + * @param amount */ public void remove(int amount) { if (count >= amount) { count -= amount; } else { - logger.warn("An attempt was made to set the counter '" + name + - "' to less than 0. Setting to 0."); + logger.warn("An attempt was made to set the counter '" + name + + "' to less than 0. Setting to 0."); count = 0; } } - /** * Returns the name of this {@link Counter} * @@ -149,20 +147,24 @@ public class Counter implements Serializable { return new Counter(this); } - @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Counter counter = (Counter) o; - if (count != counter.count) return false; + if (count != counter.count) { + return false; + } return !(name != null ? !name.equals(counter.name) : counter.name != null); } - @Override public int hashCode() { int result = name != null ? name.hashCode() : 0;