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;
         }