diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java index 5e8e633debe..b607d22bce6 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java @@ -35,14 +35,20 @@ import mage.constants.Zone; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; +import mage.target.targetpointer.FixedTarget; /** * @@ -91,27 +97,98 @@ class HinderEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { UUID objectId = source.getFirstTarget(); UUID sourceId = source.getSourceId(); - + // counter code from Spellstack StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - if (stackObject instanceof Spell) { - game.rememberLKI(objectId, Zone.STACK, (Spell) stackObject); - } - game.getStack().remove(stackObject); - MageObject targetObject = game.getObject(stackObject.getSourceId()); - Zone targetZone = Zone.LIBRARY; - if (targetObject instanceof Card) { - Card card = (Card) targetObject; - Player player = game.getPlayer(source.getControllerId()); - boolean top = player.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on top of the library? Otherwise it will be put on the bottom.", game); - card.moveToZone(targetZone, sourceId, game, top); - game.informPlayers(player.getName() + " has put " + card.getName() + " on " + (top ? "top" : "the bottom") + " of the library."); + MageObject sourceObject = game.getObject(sourceId); + if (stackObject != null && sourceObject != null) { + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { + if ( stackObject instanceof Spell ) { + game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject); + } + // Hinder specific code + ReplacementEffectImpl effect = new HinderReplacementEffect(); + effect.setTargetPointer(new FixedTarget(stackObject.getId())); + game.addEffect(effect, source); + // Hinder specific code end + game.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString()); + game.getStack().remove(stackObject); + stackObject.counter(sourceId, game); // tries to move to graveyard + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); } else { - game.informPlayers("Server: Couldn't move card to zone = " + targetZone + " as it has other than Card type."); + game.informPlayers(new StringBuilder(stackObject.getName()).append(" could not be countered by ").append(sourceObject.getLogName()).toString()); } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); return true; } + return false; + // counter code from Spellstack end + } +} + +class HinderReplacementEffect extends ReplacementEffectImpl { + + private PhaseStep phaseStep; + + public HinderReplacementEffect() { + super(Duration.OneUse, Outcome.Benefit); + staticText = "If that spell is countered this way, put that card on the top or bottom of its owner's library instead of into that player's graveyard"; + phaseStep = null; + } + + public HinderReplacementEffect(final HinderReplacementEffect effect) { + super(effect); + phaseStep = effect.phaseStep; + } + + @Override + public HinderReplacementEffect copy() { + return new HinderReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (!game.getPhase().getStep().getType().equals(phaseStep)) { + return true; + } + return super.isInactive(source, game); + } + + @Override + public void init(Ability source, Game game) { + phaseStep = game.getPhase().getStep().getType(); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + MageObject targetObject = game.getObject(event.getTargetId()); + if (targetObject instanceof Card) { + Card card = (Card) targetObject; + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + boolean top = player.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on top of the library? Otherwise it will be put on the bottom.", game); + if (card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, top, event.getAppliedEffects())) { + game.informPlayers(player.getName() + " has put " + card.getName() + " on " + (top ? "top" : "the bottom") + " of the library."); + } + return true; + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone().equals(Zone.GRAVEYARD)) { + MageObject mageObject = game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.STACK); + if (mageObject instanceof Spell) { + return ((Spell)mageObject).getSourceId().equals(event.getTargetId()); + } + } return false; } + } diff --git a/Mage.Sets/src/mage/sets/commander/MagusOfTheVineyard.java b/Mage.Sets/src/mage/sets/commander/MagusOfTheVineyard.java index faad6d3d6f9..c258310c31f 100644 --- a/Mage.Sets/src/mage/sets/commander/MagusOfTheVineyard.java +++ b/Mage.Sets/src/mage/sets/commander/MagusOfTheVineyard.java @@ -56,7 +56,7 @@ public class MagusOfTheVineyard extends CardImpl { // At the beginning of each player's precombat main phase, add {G}{G} to that player's mana pool. this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( - Zone.BATTLEFIELD, new AddManaToManaPoolEffect(GreenMana(2)), TargetController.ANY, false, true)); + Zone.BATTLEFIELD, new AddManaToManaPoolEffect(GreenMana(2), "that player's"), TargetController.ANY, false, true)); } public MagusOfTheVineyard(final MagusOfTheVineyard card) { diff --git a/Mage.Sets/src/mage/sets/commander/SpellCrumple.java b/Mage.Sets/src/mage/sets/commander/SpellCrumple.java new file mode 100644 index 00000000000..8672d70ff5a --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander/SpellCrumple.java @@ -0,0 +1,195 @@ +/* + * 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.sets.commander; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ReturnToLibrarySpellEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.Rarity; +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.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.TargetSpell; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX2 + */ +public class SpellCrumple extends CardImpl { + + public SpellCrumple(UUID ownerId) { + super(ownerId, 63, "Spell Crumple", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); + this.expansionSetCode = "CMD"; + + this.color.setBlue(true); + + // Counter target spell. If that spell is countered this way, put it on the bottom of its owner's library instead of into that player's graveyard. Put Spell Crumple on the bottom of its owner's library. + this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().addEffect(new SpellCrumpleCounterEffect()); + this.getSpellAbility().addEffect(new ReturnToLibrarySpellEffect(false)); + } + + public SpellCrumple(final SpellCrumple card) { + super(card); + } + + @Override + public SpellCrumple copy() { + return new SpellCrumple(this); + } +} + +class SpellCrumpleCounterEffect extends OneShotEffect { + + public SpellCrumpleCounterEffect() { + super(Outcome.Benefit); + this.staticText = "Counter target spell. If that spell is countered this way, put it on the bottom of its owner's library instead of into that player's graveyard"; + } + + public SpellCrumpleCounterEffect(final SpellCrumpleCounterEffect effect) { + super(effect); + } + + @Override + public SpellCrumpleCounterEffect copy() { + return new SpellCrumpleCounterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID objectId = source.getFirstTarget(); + UUID sourceId = source.getSourceId(); + // counter code from SpellStack + StackObject stackObject = game.getStack().getStackObject(objectId); + MageObject sourceObject = game.getObject(sourceId); + if (stackObject != null && sourceObject != null) { + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { + if ( stackObject instanceof Spell ) { + game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject); + } + // Spell Crumple specific code + ReplacementEffectImpl effect = new SpellCrumpleReplacementEffect(); + effect.setTargetPointer(new FixedTarget(stackObject.getId())); + game.addEffect(effect, source); + // Spell Crumple specific code end + game.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString()); + game.getStack().remove(stackObject); + stackObject.counter(sourceId, game); // tries to move to graveyard + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); + } else { + game.informPlayers(new StringBuilder(stackObject.getName()).append(" could not be countered by ").append(sourceObject.getLogName()).toString()); + } + return true; + } + return false; + // counter code from SpellStack end + } +} + +class SpellCrumpleReplacementEffect extends ReplacementEffectImpl { + + private PhaseStep phaseStep; + + public SpellCrumpleReplacementEffect() { + super(Duration.OneUse, Outcome.Benefit); + staticText = "If that spell is countered this way, put it on the bottom of its owner's library instead of into that player's graveyard"; + phaseStep = null; + } + + public SpellCrumpleReplacementEffect(final SpellCrumpleReplacementEffect effect) { + super(effect); + phaseStep = effect.phaseStep; + } + + @Override + public SpellCrumpleReplacementEffect copy() { + return new SpellCrumpleReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (!game.getPhase().getStep().getType().equals(phaseStep)) { + return true; + } + return super.isInactive(source, game); + } + + @Override + public void init(Ability source, Game game) { + phaseStep = game.getPhase().getStep().getType(); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + MageObject targetObject = game.getObject(event.getTargetId()); + if (targetObject instanceof Card) { + Card card = (Card) targetObject; + if (card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false, event.getAppliedEffects())) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + game.informPlayers(controller.getName() + " has put " + card.getName() + " on the bottom of the library."); + } + return true; + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone().equals(Zone.GRAVEYARD)) { + MageObject mageObject = game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.STACK); + if (mageObject instanceof Spell) { + return ((Spell)mageObject).getSourceId().equals(event.getTargetId()); + } + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java b/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java index 1cee1a75157..acf324cac2e 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java @@ -35,7 +35,8 @@ import mage.constants.Rarity; import mage.constants.TargetController; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheBeginOfPreCombatMainDelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOMainPhaseDelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOMainPhaseDelayedTriggeredAbility.PhaseSelection; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ManaEffect; import mage.cards.CardImpl; @@ -91,18 +92,19 @@ class PlasmCaptureCounterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean result = false; Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (spell != null) { - result = game.getStack().counter(getTargetPointer().getFirst(game, source), source.getSourceId(), game); + game.getStack().counter(getTargetPointer().getFirst(game, source), source.getSourceId(), game); + // mana gets added also if counter is not successful int mana = spell.getConvertedManaCost(); - AtTheBeginOfPreCombatMainDelayedTriggeredAbility delayedAbility = - new AtTheBeginOfPreCombatMainDelayedTriggeredAbility(new PlasmCaptureManaEffect(mana), TargetController.YOU); + AtTheBeginOMainPhaseDelayedTriggeredAbility delayedAbility = + new AtTheBeginOMainPhaseDelayedTriggeredAbility(new PlasmCaptureManaEffect(mana), TargetController.YOU, PhaseSelection.NEXT_PRECOMBAT_MAIN); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); game.addDelayedTriggeredAbility(delayedAbility); + return true; } - return result; + return false; } } @@ -152,11 +154,8 @@ class PlasmCaptureManaEffect extends ManaEffect { } } - - if (mana != null) { - player.getManaPool().addMana(mana, game, source); - return true; - } + player.getManaPool().addMana(mana, game, source); + return true; } return false; diff --git a/Mage.Sets/src/mage/sets/legends/ManaDrain.java b/Mage.Sets/src/mage/sets/legends/ManaDrain.java new file mode 100644 index 00000000000..911a79cf409 --- /dev/null +++ b/Mage.Sets/src/mage/sets/legends/ManaDrain.java @@ -0,0 +1,54 @@ +/* + * 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.sets.legends; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LevelX2 + */ +public class ManaDrain extends mage.sets.vintagemasters.ManaDrain { + + public ManaDrain(UUID ownerId) { + super(ownerId); + this.cardNumber = 65; + this.expansionSetCode = "LEG"; + this.rarity = Rarity.UNCOMMON; + } + + public ManaDrain(final ManaDrain card) { + super(card); + } + + @Override + public ManaDrain copy() { + return new ManaDrain(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/EladamrisVineyard.java b/Mage.Sets/src/mage/sets/tempest/EladamrisVineyard.java index 63872a8edc4..ed27cae6ac6 100644 --- a/Mage.Sets/src/mage/sets/tempest/EladamrisVineyard.java +++ b/Mage.Sets/src/mage/sets/tempest/EladamrisVineyard.java @@ -51,7 +51,7 @@ public class EladamrisVineyard extends CardImpl { // At the beginning of each player's precombat main phase, add {G}{G} to that player's mana pool. this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( - Zone.BATTLEFIELD, new AddManaToManaPoolEffect(GreenMana(2)), TargetController.ANY, false, true)); + Zone.BATTLEFIELD, new AddManaToManaPoolEffect(GreenMana(2), "that player's"), TargetController.ANY, false, true)); } public EladamrisVineyard(final EladamrisVineyard card) { diff --git a/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java b/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java new file mode 100644 index 00000000000..6286734d9e0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java @@ -0,0 +1,109 @@ +/* + * 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.sets.vintagemasters; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOMainPhaseDelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOMainPhaseDelayedTriggeredAbility.PhaseSelection; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AddManaToManaPoolEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.TargetSpell; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX2 + */ +public class ManaDrain extends CardImpl { + + public ManaDrain(UUID ownerId) { + super(ownerId, 78, "Mana Drain", Rarity.MYTHIC, new CardType[]{CardType.INSTANT}, "{U}{U}"); + this.expansionSetCode = "VMA"; + + this.color.setBlue(true); + + // Counter target spell. At the beginning of your next main phase, add {X} to your mana pool, where X is that spell's converted mana cost. + this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().addEffect(new ManaDrainCounterEffect()); + } + + public ManaDrain(final ManaDrain card) { + super(card); + } + + @Override + public ManaDrain copy() { + return new ManaDrain(this); + } +} + +class ManaDrainCounterEffect extends OneShotEffect { + + public ManaDrainCounterEffect() { + super(Outcome.Benefit); + this.staticText = "Counter target spell. At the beginning of your next main phase, add {X} to your mana pool, where X is that spell's converted mana cost"; + } + + public ManaDrainCounterEffect(final ManaDrainCounterEffect effect) { + super(effect); + } + + @Override + public ManaDrainCounterEffect copy() { + return new ManaDrainCounterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); + if (spell != null) { + game.getStack().counter(getTargetPointer().getFirst(game, source), source.getSourceId(), game); + // mana gets added also if counter is not successful + int cmc = spell.getConvertedManaCost(); + Effect effect = new AddManaToManaPoolEffect(new Mana(0,0,0,0,0,cmc,0), "your"); + effect.setTargetPointer(new FixedTarget(source.getControllerId())); + AtTheBeginOMainPhaseDelayedTriggeredAbility delayedAbility = + new AtTheBeginOMainPhaseDelayedTriggeredAbility(effect, TargetController.YOU, PhaseSelection.NEXT_MAIN); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + game.addDelayedTriggeredAbility(delayedAbility); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/zendikar/NissasChosen.java b/Mage.Sets/src/mage/sets/zendikar/NissasChosen.java index 350f9557b77..0a1bc9ac5a5 100644 --- a/Mage.Sets/src/mage/sets/zendikar/NissasChosen.java +++ b/Mage.Sets/src/mage/sets/zendikar/NissasChosen.java @@ -37,7 +37,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.cards.Card; import mage.cards.CardImpl; import mage.game.Game; import mage.game.events.GameEvent; diff --git a/Mage/src/mage/MageObjectReference.java b/Mage/src/mage/MageObjectReference.java index 8a023c5d84c..a110de68cef 100644 --- a/Mage/src/mage/MageObjectReference.java +++ b/Mage/src/mage/MageObjectReference.java @@ -99,6 +99,10 @@ public class MageObjectReference implements Comparable { return hash; } + public boolean refersTo(UUID id, Game game) { + return refersTo(game.getObject(id)); + } + public boolean refersTo(Permanent permanent) { return permanent.getZoneChangeCounter()== zoneChangeCounter && permanent.getId().equals(sourceId); } diff --git a/Mage/src/mage/abilities/common/delayed/AtTheBeginOfPreCombatMainDelayedTriggeredAbility.java b/Mage/src/mage/abilities/common/delayed/AtTheBeginOMainPhaseDelayedTriggeredAbility.java similarity index 61% rename from Mage/src/mage/abilities/common/delayed/AtTheBeginOfPreCombatMainDelayedTriggeredAbility.java rename to Mage/src/mage/abilities/common/delayed/AtTheBeginOMainPhaseDelayedTriggeredAbility.java index 48710e76468..6723e69b722 100644 --- a/Mage/src/mage/abilities/common/delayed/AtTheBeginOfPreCombatMainDelayedTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/delayed/AtTheBeginOMainPhaseDelayedTriggeredAbility.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.common.delayed; import mage.constants.TargetController; @@ -33,35 +32,57 @@ import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; /** * * @author LevelX2 */ +public class AtTheBeginOMainPhaseDelayedTriggeredAbility extends DelayedTriggeredAbility { -public class AtTheBeginOfPreCombatMainDelayedTriggeredAbility extends DelayedTriggeredAbility { + public enum PhaseSelection { - private TargetController targetController; + NEXT_PRECOMBAT_MAIN("next precombat"), + NEXT_POSTCOMAT_MAIN("next postcombat"), + NEXT_MAIN("next"); - public AtTheBeginOfPreCombatMainDelayedTriggeredAbility(Effect effect, TargetController targetController) { - super(effect); - this.targetController = targetController; + private final String text; + + PhaseSelection(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } } - public AtTheBeginOfPreCombatMainDelayedTriggeredAbility(AtTheBeginOfPreCombatMainDelayedTriggeredAbility ability) { + private final TargetController targetController; + private final PhaseSelection phaseSelection; + + public AtTheBeginOMainPhaseDelayedTriggeredAbility(Effect effect, TargetController targetController, PhaseSelection phaseSelection) { + super(effect); + this.targetController = targetController; + this.phaseSelection = phaseSelection; + + } + + public AtTheBeginOMainPhaseDelayedTriggeredAbility(final AtTheBeginOMainPhaseDelayedTriggeredAbility ability) { super(ability); this.targetController = ability.targetController; + this.phaseSelection = ability.phaseSelection; } @Override - public AtTheBeginOfPreCombatMainDelayedTriggeredAbility copy() { - return new AtTheBeginOfPreCombatMainDelayedTriggeredAbility(this); + public AtTheBeginOMainPhaseDelayedTriggeredAbility copy() { + return new AtTheBeginOMainPhaseDelayedTriggeredAbility(this); } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.PRECOMBAT_MAIN_PHASE_PRE) { + if (checkPhase(event.getType())) { switch (targetController) { case ANY: return true; @@ -87,21 +108,34 @@ public class AtTheBeginOfPreCombatMainDelayedTriggeredAbility extends DelayedTri return false; } + private boolean checkPhase(EventType eventType) { + switch (phaseSelection) { + case NEXT_MAIN: + return EventType.PRECOMBAT_MAIN_PHASE_PRE.equals(eventType) || EventType.PRECOMBAT_MAIN_PHASE_POST.equals(eventType); + case NEXT_POSTCOMAT_MAIN: + return EventType.PRECOMBAT_MAIN_PHASE_POST.equals(eventType); + case NEXT_PRECOMBAT_MAIN: + return EventType.PRECOMBAT_MAIN_PHASE_PRE.equals(eventType); + default: + return false; + } + } + @Override public String getRule() { StringBuilder sb = new StringBuilder(); switch (targetController) { case YOU: - sb.append("At the beginning of your next precombat main phase, "); + sb.append("At the beginning of your ").append(phaseSelection.toString()).append(" main phase, "); break; case OPPONENT: - sb.append("At the beginning of an opponent's next precombat main phase, "); + sb.append("At the beginning of an opponent's ").append(phaseSelection.toString()).append(" main phase, "); break; case ANY: - sb.append("At the beginning of the next precombat main phase, "); + sb.append("At the beginning of the ").append(phaseSelection.toString()).append(" main phase, "); break; case CONTROLLER_ATTACHED_TO: - sb.append("At the beginning of the next precombat main phase of enchanted creature's controller, "); + sb.append("At the beginning of the ").append(phaseSelection.toString()).append(" main phase of enchanted creature's controller, "); break; } sb.append(getEffects().getText(modes.getMode())); diff --git a/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java b/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java index a55132b6b81..1cf0b527898 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java @@ -22,10 +22,10 @@ public class AddManaToManaPoolEffect extends OneShotEffect { protected Mana mana; - public AddManaToManaPoolEffect(Mana mana) { + public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner) { super(Outcome.PutManaInPool); this.mana = mana; - this.staticText = "add " + mana.toString() + " to that player's mana pool"; + this.staticText = "add " + mana.toString() + " to " + textManaPoolOwner + " mana pool"; } public AddManaToManaPoolEffect(final AddManaToManaPoolEffect effect) { diff --git a/Mage/src/mage/abilities/effects/common/ReturnToHandSpellEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToHandSpellEffect.java index c436a41c6c6..dd0233bcddc 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToHandSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToHandSpellEffect.java @@ -9,6 +9,7 @@ import mage.game.Game; import java.io.ObjectStreamException; import java.util.UUID; +import mage.players.Player; /** * @@ -41,7 +42,10 @@ public class ReturnToHandSpellEffect extends PostResolveEffect implements MageSi @Override public void postResolve(Card card, Ability source, UUID controllerId, Game game) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + Player controller = game.getPlayer(controllerId); + if (controller != null) { + controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.STACK); + } } } diff --git a/Mage/src/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java new file mode 100644 index 00000000000..d63d35b3007 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ReturnToLibrarySpellEffect.java @@ -0,0 +1,73 @@ +/* + * 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 java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.PostResolveEffect; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class ReturnToLibrarySpellEffect extends PostResolveEffect { + + private final boolean toTop; + + public ReturnToLibrarySpellEffect(boolean top) { + staticText = "Put {this} on "+ (top ? "top":"the bottom") + " of its owner's library"; + this.toTop = top; + } + + public ReturnToLibrarySpellEffect(final ReturnToLibrarySpellEffect effect) { + super(effect); + this.toTop = effect.toTop; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ReturnToLibrarySpellEffect copy() { + return new ReturnToLibrarySpellEffect(this); + } + + @Override + public void postResolve(Card card, Ability source, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.STACK, toTop, true); + } + } +} diff --git a/Mage/src/mage/game/stack/SpellStack.java b/Mage/src/mage/game/stack/SpellStack.java index 14fb35ddf10..950254b930a 100644 --- a/Mage/src/mage/game/stack/SpellStack.java +++ b/Mage/src/mage/game/stack/SpellStack.java @@ -73,6 +73,9 @@ public class SpellStack extends ArrayDeque { } public boolean counter(UUID objectId, UUID sourceId, Game game) { + // the counter logic is copied by some spells to handle replacement effects of the countered spell + // so if logic is changed here check those spells for needed changes too + // Concerned cards to check: Hinder, Spell Crumple StackObject stackObject = getStackObject(objectId); MageObject sourceObject = game.getObject(sourceId); if (stackObject != null && sourceObject != null) { @@ -81,7 +84,7 @@ public class SpellStack extends ArrayDeque { game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject); } this.remove(stackObject); - stackObject.counter(sourceId, game); + stackObject.counter(sourceId, game); // tries to move to graveyard game.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString()); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); } else {