Added Mana Drain and Spell Crumple. Fixed replacement effect of Hinder.

This commit is contained in:
LevelX2 2014-08-25 13:54:47 +02:00
parent 97276cbd2a
commit 48e8be4fc2
14 changed files with 599 additions and 48 deletions

View file

@ -35,14 +35,20 @@ import mage.constants.Zone;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.Duration;
import mage.constants.PhaseStep;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; 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.Spell;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetSpell; import mage.target.TargetSpell;
import mage.target.targetpointer.FixedTarget;
/** /**
* *
@ -91,27 +97,98 @@ class HinderEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
UUID objectId = source.getFirstTarget(); UUID objectId = source.getFirstTarget();
UUID sourceId = source.getSourceId(); UUID sourceId = source.getSourceId();
// counter code from Spellstack
StackObject stackObject = game.getStack().getStackObject(objectId); StackObject stackObject = game.getStack().getStackObject(objectId);
if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { MageObject sourceObject = game.getObject(sourceId);
if (stackObject instanceof Spell) { if (stackObject != null && sourceObject != null) {
game.rememberLKI(objectId, Zone.STACK, (Spell) stackObject); if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
} if ( stackObject instanceof Spell ) {
game.getStack().remove(stackObject); game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
MageObject targetObject = game.getObject(stackObject.getSourceId()); }
Zone targetZone = Zone.LIBRARY; // Hinder specific code
if (targetObject instanceof Card) { ReplacementEffectImpl effect = new HinderReplacementEffect();
Card card = (Card) targetObject; effect.setTargetPointer(new FixedTarget(stackObject.getId()));
Player player = game.getPlayer(source.getControllerId()); game.addEffect(effect, source);
boolean top = player.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on top of the library? Otherwise it will be put on the bottom.", game); // Hinder specific code end
card.moveToZone(targetZone, sourceId, game, top); game.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString());
game.informPlayers(player.getName() + " has put " + card.getName() + " on " + (top ? "top" : "the bottom") + " of the library."); 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 { } 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 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; return false;
} }
} }

View file

@ -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. // At the beginning of each player's precombat main phase, add {G}{G} to that player's mana pool.
this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( 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) { public MagusOfTheVineyard(final MagusOfTheVineyard card) {

View file

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

View file

@ -35,7 +35,8 @@ import mage.constants.Rarity;
import mage.constants.TargetController; import mage.constants.TargetController;
import mage.Mana; import mage.Mana;
import mage.abilities.Ability; 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.OneShotEffect;
import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.common.ManaEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -91,18 +92,19 @@ class PlasmCaptureCounterEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
boolean result = false;
Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
if (spell != null) { 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(); int mana = spell.getConvertedManaCost();
AtTheBeginOfPreCombatMainDelayedTriggeredAbility delayedAbility = AtTheBeginOMainPhaseDelayedTriggeredAbility delayedAbility =
new AtTheBeginOfPreCombatMainDelayedTriggeredAbility(new PlasmCaptureManaEffect(mana), TargetController.YOU); new AtTheBeginOMainPhaseDelayedTriggeredAbility(new PlasmCaptureManaEffect(mana), TargetController.YOU, PhaseSelection.NEXT_PRECOMBAT_MAIN);
delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setControllerId(source.getControllerId());
game.addDelayedTriggeredAbility(delayedAbility); game.addDelayedTriggeredAbility(delayedAbility);
return true;
} }
return result; return false;
} }
} }
@ -152,11 +154,8 @@ class PlasmCaptureManaEffect extends ManaEffect {
} }
} }
player.getManaPool().addMana(mana, game, source);
if (mana != null) { return true;
player.getManaPool().addMana(mana, game, source);
return true;
}
} }
return false; return false;

View file

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

View file

@ -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. // At the beginning of each player's precombat main phase, add {G}{G} to that player's mana pool.
this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( 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) { public EladamrisVineyard(final EladamrisVineyard card) {

View file

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

View file

@ -37,7 +37,6 @@ import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;

View file

@ -99,6 +99,10 @@ public class MageObjectReference implements Comparable<MageObjectReference> {
return hash; return hash;
} }
public boolean refersTo(UUID id, Game game) {
return refersTo(game.getObject(id));
}
public boolean refersTo(Permanent permanent) { public boolean refersTo(Permanent permanent) {
return permanent.getZoneChangeCounter()== zoneChangeCounter && permanent.getId().equals(sourceId); return permanent.getZoneChangeCounter()== zoneChangeCounter && permanent.getId().equals(sourceId);
} }

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.abilities.common.delayed; package mage.abilities.common.delayed;
import mage.constants.TargetController; import mage.constants.TargetController;
@ -33,35 +32,57 @@ import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
/** /**
* *
* @author LevelX2 * @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) { private final String text;
super(effect);
this.targetController = targetController; 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); super(ability);
this.targetController = ability.targetController; this.targetController = ability.targetController;
this.phaseSelection = ability.phaseSelection;
} }
@Override @Override
public AtTheBeginOfPreCombatMainDelayedTriggeredAbility copy() { public AtTheBeginOMainPhaseDelayedTriggeredAbility copy() {
return new AtTheBeginOfPreCombatMainDelayedTriggeredAbility(this); return new AtTheBeginOMainPhaseDelayedTriggeredAbility(this);
} }
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.PRECOMBAT_MAIN_PHASE_PRE) { if (checkPhase(event.getType())) {
switch (targetController) { switch (targetController) {
case ANY: case ANY:
return true; return true;
@ -87,21 +108,34 @@ public class AtTheBeginOfPreCombatMainDelayedTriggeredAbility extends DelayedTri
return false; 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 @Override
public String getRule() { public String getRule() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
switch (targetController) { switch (targetController) {
case YOU: 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; break;
case OPPONENT: 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; break;
case ANY: 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; break;
case CONTROLLER_ATTACHED_TO: 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; break;
} }
sb.append(getEffects().getText(modes.getMode())); sb.append(getEffects().getText(modes.getMode()));

View file

@ -22,10 +22,10 @@ public class AddManaToManaPoolEffect extends OneShotEffect {
protected Mana mana; protected Mana mana;
public AddManaToManaPoolEffect(Mana mana) { public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner) {
super(Outcome.PutManaInPool); super(Outcome.PutManaInPool);
this.mana = mana; 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) { public AddManaToManaPoolEffect(final AddManaToManaPoolEffect effect) {

View file

@ -9,6 +9,7 @@ import mage.game.Game;
import java.io.ObjectStreamException; import java.io.ObjectStreamException;
import java.util.UUID; import java.util.UUID;
import mage.players.Player;
/** /**
* *
@ -41,7 +42,10 @@ public class ReturnToHandSpellEffect extends PostResolveEffect implements MageSi
@Override @Override
public void postResolve(Card card, Ability source, UUID controllerId, Game game) { 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);
}
} }
} }

View file

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

View file

@ -73,6 +73,9 @@ public class SpellStack extends ArrayDeque<StackObject> {
} }
public boolean counter(UUID objectId, UUID sourceId, Game game) { 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); StackObject stackObject = getStackObject(objectId);
MageObject sourceObject = game.getObject(sourceId); MageObject sourceObject = game.getObject(sourceId);
if (stackObject != null && sourceObject != null) { if (stackObject != null && sourceObject != null) {
@ -81,7 +84,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject); game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
} }
this.remove(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.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString());
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));
} else { } else {