From b094df2a52719f7ee720f7831053ce9e17545ee7 Mon Sep 17 00:00:00 2001 From: LevelX2 <ludwig.hirth@online.de> Date: Thu, 21 Aug 2014 15:13:29 +0200 Subject: [PATCH] Fixed some put into graveyard effects that were implemented wrongly as triggered effects to replacment effects (LegacyWeapon, Progenitus, Darksteel Colossus, Blightsteel Colossus). --- .../mage/sets/apocalypse/LegacyWeapon.java | 47 +---- .../src/mage/sets/conflux/Progenitus.java | 48 +---- .../sets/darksteel/DarksteelColossus.java | 54 +----- .../mage/sets/innistrad/LilianaOfTheVeil.java | 2 + .../mirrodinbesieged/BlightsteelColossus.java | 54 +----- .../sets/returntoravnica/DryadMilitant.java | 2 +- .../sets/returntoravnica/RestInPeace.java | 5 +- .../ZoneChangeReplacementTest.java | 133 +++++++++++++ .../PutIntoGraveFromAnywhereAbility.java | 176 ++++++++++++++++++ ...vealAndShuffleIntoLibrarySourceEffect.java | 82 ++++++++ Mage/src/mage/players/PlayerImpl.java | 7 +- 11 files changed, 424 insertions(+), 186 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/ZoneChangeReplacementTest.java create mode 100644 Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereAbility.java create mode 100644 Mage/src/mage/abilities/effects/common/RevealAndShuffleIntoLibrarySourceEffect.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/LegacyWeapon.java b/Mage.Sets/src/mage/sets/apocalypse/LegacyWeapon.java index 543c069303..a035cba624 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/LegacyWeapon.java +++ b/Mage.Sets/src/mage/sets/apocalypse/LegacyWeapon.java @@ -29,21 +29,14 @@ package mage.sets.apocalypse; import java.util.UUID; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.common.PutIntoGraveFromAnywhereTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.RevealAndShuffleIntoLibrarySourceEffect; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPermanent; /** @@ -64,7 +57,7 @@ public class LegacyWeapon extends CardImpl { ability.addTarget(new TargetPermanent()); this.addAbility(ability); // If Legacy Weapon would be put into a graveyard from anywhere, reveal Legacy Weapon and shuffle it into its owner's library instead. - this.addAbility(new PutIntoGraveFromAnywhereTriggeredAbility(new LegacyWeaponEffect())); + this.addAbility(new PutIntoGraveFromAnywhereAbility(new RevealAndShuffleIntoLibrarySourceEffect())); } public LegacyWeapon(final LegacyWeapon card) { @@ -76,37 +69,3 @@ public class LegacyWeapon extends CardImpl { return new LegacyWeapon(this); } } - -class LegacyWeaponEffect extends OneShotEffect { - - public LegacyWeaponEffect() { - super(Outcome.Benefit); - this.staticText = "reveal {this} and shuffle it into its owner's library instead"; - } - - public LegacyWeaponEffect(final LegacyWeaponEffect effect) { - super(effect); - } - - @Override - public LegacyWeaponEffect copy() { - return new LegacyWeaponEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - Cards cards = new CardsImpl(); - cards.add(card); - player.revealCards("Legacy Weapon", cards, game); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - player.shuffleLibrary(game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/conflux/Progenitus.java b/Mage.Sets/src/mage/sets/conflux/Progenitus.java index 5663966842..46cb6f8fac 100644 --- a/Mage.Sets/src/mage/sets/conflux/Progenitus.java +++ b/Mage.Sets/src/mage/sets/conflux/Progenitus.java @@ -30,21 +30,14 @@ package mage.sets.conflux; import java.util.UUID; import mage.MageInt; import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.PutIntoGraveFromAnywhereTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.PutIntoGraveFromAnywhereAbility; +import mage.abilities.effects.common.RevealAndShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.ProtectionAbility; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; -import mage.players.Player; /** * @@ -72,10 +65,11 @@ public class Progenitus extends CardImpl { // spells or abilities, and all damage that would be dealt to Progenitus is prevented. // 2/1/2009: Progenitus can still be affected by effects that don't target it or deal damage // to it (such as Day of Judgment). + // Protection from everything this.addAbility(new ProgenitusProtectionAbility()); // If Progenitus would be put into a graveyard from anywhere, reveal Progenitus and shuffle it into its owner's library instead. - this.addAbility(new PutIntoGraveFromAnywhereTriggeredAbility(new ProgenitusEffect())); + this.addAbility(new PutIntoGraveFromAnywhereAbility(new RevealAndShuffleIntoLibrarySourceEffect())); } public Progenitus(final Progenitus card) { @@ -113,37 +107,3 @@ class ProgenitusProtectionAbility extends ProtectionAbility { return false; } } - -class ProgenitusEffect extends OneShotEffect { - - public ProgenitusEffect() { - super(Outcome.Benefit); - this.staticText = "reveal {this} and shuffle it into its owner's library instead"; - } - - public ProgenitusEffect(final ProgenitusEffect effect) { - super(effect); - } - - @Override - public ProgenitusEffect copy() { - return new ProgenitusEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - Cards cards = new CardsImpl(); - cards.add(card); - player.revealCards("Progenitus", cards, game); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - player.shuffleLibrary(game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/darksteel/DarksteelColossus.java b/Mage.Sets/src/mage/sets/darksteel/DarksteelColossus.java index 0c2d65bbf3..3a7b06bee3 100644 --- a/Mage.Sets/src/mage/sets/darksteel/DarksteelColossus.java +++ b/Mage.Sets/src/mage/sets/darksteel/DarksteelColossus.java @@ -33,19 +33,11 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.PutIntoGraveFromAnywhereTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.PutIntoGraveFromAnywhereAbility; +import mage.abilities.effects.common.RevealAndShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; /** * @author Loki @@ -58,9 +50,15 @@ public class DarksteelColossus extends CardImpl { this.subtype.add("Golem"); this.power = new MageInt(11); this.toughness = new MageInt(11); + + // Trample this.addAbility(TrampleAbility.getInstance()); + + // Darksteel Colossus is indestructible. this.addAbility(IndestructibleAbility.getInstance()); - this.addAbility(new PutIntoGraveFromAnywhereTriggeredAbility(new DarksteelColossusEffect(), false)); + + // If Darksteel Colossus would be put into a graveyard from anywhere, reveal Darksteel Colossus and shuffle it into its owner's library instead. + this.addAbility(new PutIntoGraveFromAnywhereAbility(new RevealAndShuffleIntoLibrarySourceEffect())); } public DarksteelColossus(final DarksteelColossus card) { @@ -73,37 +71,3 @@ public class DarksteelColossus extends CardImpl { } } - -class DarksteelColossusEffect extends OneShotEffect { - DarksteelColossusEffect() { - super(Outcome.Benefit); - staticText = "reveal {this} and shuffle it into its owner's library instead"; - } - - DarksteelColossusEffect(final DarksteelColossusEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Card c = game.getCard(source.getSourceId()); - if (c != null) { - Player player = game.getPlayer(c.getOwnerId()); - if (player != null) { - Cards cards = new CardsImpl(); - cards.add(c); - player.revealCards("Blightsteel Colossus", cards, game); - c.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - player.shuffleLibrary(game); - return true; - } - } - return false; - } - - @Override - public DarksteelColossusEffect copy() { - return new DarksteelColossusEffect(this); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/innistrad/LilianaOfTheVeil.java b/Mage.Sets/src/mage/sets/innistrad/LilianaOfTheVeil.java index d83989055e..e78a2a4996 100644 --- a/Mage.Sets/src/mage/sets/innistrad/LilianaOfTheVeil.java +++ b/Mage.Sets/src/mage/sets/innistrad/LilianaOfTheVeil.java @@ -68,10 +68,12 @@ public class LilianaOfTheVeil extends CardImpl { // +1: Each player discards a card. this.addAbility(new LoyaltyAbility(new DiscardEachPlayerEffect(), 1)); + // -2: Target player sacrifices a creature. LoyaltyAbility ability = new LoyaltyAbility(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player"), -2); ability.addTarget(new TargetPlayer()); this.addAbility(ability); + // -6: Separate all permanents target player controls into two piles. That player sacrifices all permanents in the pile of his or her choice. ability = new LoyaltyAbility(new LilianaOfTheVeilEffect(), -6); ability.addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/BlightsteelColossus.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/BlightsteelColossus.java index 6320849cdd..000867fb52 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/BlightsteelColossus.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/BlightsteelColossus.java @@ -33,20 +33,12 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.PutIntoGraveFromAnywhereTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.PutIntoGraveFromAnywhereAbility; +import mage.abilities.effects.common.RevealAndShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.InfectAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; /** * @author Loki @@ -59,10 +51,16 @@ public class BlightsteelColossus extends CardImpl { this.subtype.add("Golem"); this.power = new MageInt(11); this.toughness = new MageInt(11); + + // Trample, infect this.addAbility(TrampleAbility.getInstance()); this.addAbility(InfectAbility.getInstance()); + + // Blightsteel Colossus is indestructible. this.addAbility(IndestructibleAbility.getInstance()); - this.addAbility(new PutIntoGraveFromAnywhereTriggeredAbility(new BlightsteelColossusEffect(), false)); + + // If Blightsteel Colossus would be put into a graveyard from anywhere, reveal Blightsteel Colossus and shuffle it into its owner's library instead. + this.addAbility(new PutIntoGraveFromAnywhereAbility(new RevealAndShuffleIntoLibrarySourceEffect())); } public BlightsteelColossus(final BlightsteelColossus card) { @@ -75,37 +73,3 @@ public class BlightsteelColossus extends CardImpl { } } - -class BlightsteelColossusEffect extends OneShotEffect { - BlightsteelColossusEffect() { - super(Outcome.Benefit); - staticText = "reveal {this} and shuffle it into its owner's library"; - } - - BlightsteelColossusEffect(final BlightsteelColossusEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Card c = game.getCard(source.getSourceId()); - if (c != null) { - Player player = game.getPlayer(c.getOwnerId()); - if (player != null) { - Cards cards = new CardsImpl(); - cards.add(c); - player.revealCards("Blightsteel Colossus", cards, game); - c.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - player.shuffleLibrary(game); - return true; - } - } - return false; - } - - @Override - public BlightsteelColossusEffect copy() { - return new BlightsteelColossusEffect(this); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/returntoravnica/DryadMilitant.java b/Mage.Sets/src/mage/sets/returntoravnica/DryadMilitant.java index 0bb2ccaf84..b501cd369d 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/DryadMilitant.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/DryadMilitant.java @@ -104,7 +104,7 @@ class DryadMilitantReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD) { - Card card = (Card) game.getCard(event.getTargetId()); + Card card = game.getCard(event.getTargetId()); if (card != null && (card.getCardType().contains(CardType.SORCERY) || card.getCardType().contains(CardType.INSTANT))) { return true; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java index aef299e541..0624152a50 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java @@ -165,10 +165,7 @@ class RestInPeaceReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD) { - return true; - } - return false; + return event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD; } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ZoneChangeReplacementTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ZoneChangeReplacementTest.java new file mode 100644 index 0000000000..1ab9e1ac6f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ZoneChangeReplacementTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * Checks if change zone replacement effects work as intended + * + * @author LevelX2 + */ + +public class ZoneChangeReplacementTest extends CardTestPlayerBase { + + // If Darksteel Colossus would be put into a graveyard from anywhere, + // reveal Darksteel Colossus and shuffle it into its owner's library instead. + @Test + public void testFromLibraryZoneChange() { + addCard(Zone.LIBRARY, playerA, "Darksteel Colossus"); + // Tome Scour - Sorcery - {U} + // Target player puts the top five cards of his or her library into his or her graveyard. + addCard(Zone.HAND, playerA, "Tome Scour"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tome Scour", playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Darksteel Colossus", 0); + assertGraveyardCount(playerA, 5); // 4 + Tome Scour + + } + + @Test + public void testFromHandZoneChange() { + addCard(Zone.HAND, playerA, "Progenitus"); + // Distress - Sorcery - {B}{B} + // Target player reveals his or her hand. You choose a nonland card from it. That player discards that card. + addCard(Zone.HAND, playerA, "Distress"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Distress", playerA); + setChoice(playerA, "Progenitus"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Progenitus", 0); + assertGraveyardCount(playerA, 1); // Distress + + assertHandCount(playerA, "Progenitus", 0); + } + + @Test + public void checkBridgeDoesWork() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + // Diabolic Edict - Instant - {1}{B} + // Target player sacrifices a creature. + addCard(Zone.HAND, playerA, "Diabolic Edict"); + // Whenever a nontoken creature is put into your graveyard from the battlefield, if Bridge from + // Below is in your graveyard, put a 2/2 black Zombie creature token onto the battlefield. + addCard(Zone.GRAVEYARD, playerA, "Bridge from Below"); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerA); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, 3); // Diabolic Edict + Bridge from Below + Silvercoat Lion + assertPermanentCount(playerA, "Zombie", 1); // Silvercoat Lion goes to graveyard so a Zombie tokes is created + + } + + @Test + public void testDoesntTriggerDiesTriggeredAbilities() { + addCard(Zone.BATTLEFIELD, playerA, "Progenitus"); + // Diabolic Edict - Instant - {1}{B} + // Target player sacrifices a creature. + addCard(Zone.HAND, playerA, "Diabolic Edict"); + // Whenever a nontoken creature is put into your graveyard from the battlefield, if Bridge from + // Below is in your graveyard, put a 2/2 black Zombie creature token onto the battlefield. + addCard(Zone.GRAVEYARD, playerA, "Bridge from Below"); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerA); + setChoice(playerA, "Progenitus"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Progenitus", 0); + assertGraveyardCount(playerA, 2); // Diabolic Edict + Bridge from Below + assertPermanentCount(playerA, "Zombie", 0); // Progenitus never touches graveyard - so no Zombie tokes is created + + } +} + diff --git a/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereAbility.java b/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereAbility.java new file mode 100644 index 0000000000..4449d8c07f --- /dev/null +++ b/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereAbility.java @@ -0,0 +1,176 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.condition.Condition; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.stack.Spell; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class PutIntoGraveFromAnywhereAbility extends SimpleStaticAbility { + + public PutIntoGraveFromAnywhereAbility(Effect baseEffect) { + this(baseEffect, null, "", true, false); + } + + public PutIntoGraveFromAnywhereAbility(Effect baseEffect, Condition condition, String text, boolean selfScope, boolean optional) { + super(Zone.ALL, new PutIntoGraveFromAnywhereEffect(baseEffect, condition, text, selfScope, optional)); + } + + public PutIntoGraveFromAnywhereAbility(final PutIntoGraveFromAnywhereAbility ability) { + super(ability); + } + + @Override + public SimpleStaticAbility copy() { + return new PutIntoGraveFromAnywhereAbility(this); + } + + @Override + public String getRule() { + return "If {this} would be put into a graveyard from anywhere, " + super.getRule(); + } + +} + +class PutIntoGraveFromAnywhereEffect extends ReplacementEffectImpl { + + protected Effects baseEffects = new Effects(); + protected String text; + protected Condition condition; + protected boolean optional; + + public static final String SOURCE_CAST_SPELL_ABILITY = "sourceCastSpellAbility"; + + public PutIntoGraveFromAnywhereEffect(Effect baseEffect) { + this(baseEffect, ""); + } + + public PutIntoGraveFromAnywhereEffect(Effect baseEffect, String text) { + this(baseEffect, null, text, true, false); + } + + public PutIntoGraveFromAnywhereEffect(Effect baseEffect, String text, boolean optional) { + this(baseEffect, null, text, true, optional); + } + + public PutIntoGraveFromAnywhereEffect(Effect baseEffect, Condition condition, String text) { + this(baseEffect, condition, text, true, false); + } + + public PutIntoGraveFromAnywhereEffect(Effect baseEffect, Condition condition, String text, boolean selfScope, boolean optional) { + super(Duration.EndOfGame, baseEffect.getOutcome(), selfScope); + this.baseEffects.add(baseEffect); + this.text = text; + this.condition = condition; + this.optional = optional; + } + + public PutIntoGraveFromAnywhereEffect(PutIntoGraveFromAnywhereEffect effect) { + super(effect); + this.baseEffects = effect.baseEffects.copy(); + this.text = effect.text; + this.condition = effect.condition; + this.optional = effect.optional; + } + + public void addEffect(Effect effect) { + baseEffects.add(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (GameEvent.EventType.ZONE_CHANGE.equals(event.getType()) + && ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD + && event.getTargetId().equals(source.getSourceId())) { + if (condition == null || condition.apply(game, source)) { + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (optional) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject object = game.getObject(source.getSourceId()); + if (controller == null || object == null) { + return false; + } + if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), game)) { + return false; + } + } + Spell spell = game.getStack().getSpell(event.getSourceId()); + for (Effect effect: baseEffects) { + if (effect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) effect, source); + } + else { + if (spell != null) { + effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); + } + effect.apply(game, source); + } + } + return true; + } + + @Override + public String getText(Mode mode) { + return (text == null || text.isEmpty()) ? baseEffects.getText(mode) : text; + } + + @Override + public PutIntoGraveFromAnywhereEffect copy() { + return new PutIntoGraveFromAnywhereEffect(this); + } + +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/effects/common/RevealAndShuffleIntoLibrarySourceEffect.java b/Mage/src/mage/abilities/effects/common/RevealAndShuffleIntoLibrarySourceEffect.java new file mode 100644 index 0000000000..4fda331bcd --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/RevealAndShuffleIntoLibrarySourceEffect.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ + +public class RevealAndShuffleIntoLibrarySourceEffect extends OneShotEffect { + + public RevealAndShuffleIntoLibrarySourceEffect() { + super(Outcome.Neutral); + staticText = "reveal {this} and shuffle it into its owner's library instead"; + } + + public RevealAndShuffleIntoLibrarySourceEffect(final RevealAndShuffleIntoLibrarySourceEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Card sourceCard = game.getCard(source.getSourceId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (sourceCard != null) { + Player player = game.getPlayer(sourceCard.getOwnerId()); + if (player != null) { + Zone fromZone = game.getState().getZone(sourceCard.getId()); + Cards cards = new CardsImpl(); + cards.add(sourceCard); + player.revealCards(sourceObject.getLogName(), cards, game); + player.moveCardToLibraryWithInfo(sourceCard, source.getSourceId(), game, fromZone, true, true); + player.shuffleLibrary(game); + return true; + } + } + return false; + } + + @Override + public RevealAndShuffleIntoLibrarySourceEffect copy() { + return new RevealAndShuffleIntoLibrarySourceEffect(this); + } + +} diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 7bb2e2d2c7..3bed23eb41 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -631,13 +631,14 @@ public abstract class PlayerImpl implements Player, Serializable { the moment before the cost was paid (see rule 717, "Handling Illegal Actions"). */ if (card != null) { + // write info to game log first so game log infos from triggered or replacement effects follow in the game log + game.informPlayers(new StringBuilder(name).append(" discards ").append(card.getName()).toString()); /* If a card is discarded while Rest in Peace is on the battlefield, abilities that function * when a card is discarded (such as madness) still work, even though that card never reaches * a graveyard. In addition, spells or abilities that check the characteristics of a discarded - * card (such as Chandra Ablaze's first ability) can find that card in exile. */ + * card (such as Chandra Ablaze's first ability) can find that card in exile. */ + card.moveToZone(Zone.GRAVEYARD, source==null?null:source.getSourceId(), game, false); // So discard is also successful if card is moved to another zone by replacement effect! - card.moveToZone(Zone.GRAVEYARD, source==null?null:source.getSourceId(), game, false); - game.informPlayers(new StringBuilder(name).append(" discards ").append(card.getName()).toString()); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source==null?null:source.getSourceId(), playerId)); return true; }