* Reworked ENTERS_THE_BATTLEFIELD event for replacement effects. Some rework to card movement.

This commit is contained in:
LevelX2 2015-10-17 16:30:28 +02:00
parent c642165020
commit 59ef2a2889
247 changed files with 1842 additions and 2287 deletions

View file

@ -958,6 +958,10 @@ public abstract class AbilityImpl implements Ability {
if (object instanceof Permanent) {
return false;
} else {
Permanent permanent = game.getPermanentEntering(getSourceId());
if (permanent != null && permanent.getAbilities().contains(this)) {
return true;
}
// check if it's an ability that is temporary gained to a card
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
if (otherAbilities == null || !otherAbilities.contains(this)) {

View file

@ -71,7 +71,7 @@ public class DiesTriggeredAbility extends ZoneChangeTriggeredAbility {
if (super.checkEventType(event, game)) {
return ((ZoneChangeEvent) event).getFromZone().equals(Zone.BATTLEFIELD) && ((ZoneChangeEvent) event).getToZone().equals(Zone.GRAVEYARD);
}
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
return false;
}
@Override

View file

@ -40,44 +40,50 @@ import mage.constants.Zone;
public class EntersBattlefieldAbility extends StaticAbility {
protected String abilityRule;
protected boolean optional;
public EntersBattlefieldAbility(Effect effect) {
this(effect, true);
this(effect, false);
}
/**
*
* @param effect effect that happens when the permanent enters the
* battlefield
* @param showRule show the rule for this ability
* battlefiely
* @param optional
*/
public EntersBattlefieldAbility(Effect effect, Boolean showRule) {
this(effect, null, showRule, null, null);
public EntersBattlefieldAbility(Effect effect, boolean optional) {
this(effect, optional, null, null, null);
}
public EntersBattlefieldAbility(Effect effect, String effectText) {
this(effect, null, true, null, effectText);
this(effect, null, null, effectText);
}
public EntersBattlefieldAbility(Effect effect, Condition condition, String abilityRule, String effectText) {
this(effect, false, condition, abilityRule, effectText);
}
/**
*
* @param effect effect that happens when the permanent enters the
* battlefield
* @param optional
* @param condition only if this condition is true, the effect will happen
* @param ruleVisible show the rule for this ability
* @param abilityRule rule for this ability (no text from effects will be
* added)
* @param effectText this text will be used for the EnterBattlefieldEffect
*/
public EntersBattlefieldAbility(Effect effect, Condition condition, Boolean ruleVisible, String abilityRule, String effectText) {
super(Zone.ALL, new EntersBattlefieldEffect(effect, condition, effectText));
this.setRuleVisible(ruleVisible);
public EntersBattlefieldAbility(Effect effect, boolean optional, Condition condition, String abilityRule, String effectText) {
super(Zone.ALL, new EntersBattlefieldEffect(effect, condition, effectText, true, optional));
this.abilityRule = abilityRule;
this.optional = optional;
}
public EntersBattlefieldAbility(final EntersBattlefieldAbility ability) {
super(ability);
this.abilityRule = ability.abilityRule;
this.optional = ability.optional;
}
@Override
@ -99,12 +105,9 @@ public class EntersBattlefieldAbility extends StaticAbility {
@Override
public String getRule() {
if (!ruleVisible) {
return "";
}
if (abilityRule != null && !abilityRule.isEmpty()) {
return abilityRule;
}
return "{this} enters the battlefield " + super.getRule();
return (optional ? "you may have " : "") + "{this} enter" + (optional ? "" : "s") + " the battlefield " + super.getRule();
}
}

View file

@ -0,0 +1,30 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.common;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.counters.CounterType;
/**
*
* @author LevelX2
*/
public class PlanswalkerEntersWithLoyalityCountersAbility extends EntersBattlefieldAbility {
public PlanswalkerEntersWithLoyalityCountersAbility(int loyality) {
super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyality)));
setRuleVisible(false);
}
public PlanswalkerEntersWithLoyalityCountersAbility(final PlanswalkerEntersWithLoyalityCountersAbility ability) {
super(ability);
}
@Override
public EntersBattlefieldAbility copy() {
return new PlanswalkerEntersWithLoyalityCountersAbility(this);
}
}

View file

@ -12,10 +12,14 @@ import mage.watchers.Watcher;
* @author Loki
*/
public class CastFromHandCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
Permanent p = game.getPermanent(source.getSourceId());
if (p != null) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = game.getPermanentEntering(source.getSourceId());
}
if (permanent != null) {
Watcher watcher = game.getState().getWatchers().get("CastFromHand", source.getSourceId());
if (watcher != null && watcher.conditionMet()) {
return true;
@ -29,5 +33,4 @@ public class CastFromHandCondition implements Condition {
return "you cast it from your hand";
}
}

View file

@ -125,8 +125,6 @@ public class AlternativeCost2Impl<T extends AlternativeCost2Impl<T>> extends Cos
activated = true;
}
;
/**
* Reset the activate and count information
*

View file

@ -879,7 +879,7 @@ public class ContinuousEffects implements Serializable {
// For example: Vesuva copying a Dark Depth (VesuvaTest:testDarkDepth)
// This call should be removed if possible as replacement effects of EntersTheBattlefield events
// do no longer work correctly because the entering permanents are not yet on the battlefield (before they were).
game.applyEffects();
// game.applyEffects();
} while (true);
return caught;
}

View file

@ -34,7 +34,6 @@ import mage.abilities.condition.Condition;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.Spell;
@ -52,7 +51,6 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
protected Condition condition;
protected boolean optional;
public static final String ENTERING_PERMANENT = "enteringPermanent";
public static final String SOURCE_CAST_SPELL_ABILITY = "sourceCastSpellAbility";
public EntersBattlefieldEffect(Effect baseEffect) {
@ -67,10 +65,6 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
this(baseEffect, null, text, true, optional);
}
public EntersBattlefieldEffect(Effect baseEffect, Condition condition, String text) {
this(baseEffect, condition, text, true, false);
}
public EntersBattlefieldEffect(Effect baseEffect, Condition condition, String text, boolean selfScope, boolean optional) {
super(Duration.WhileOnBattlefield, baseEffect.getOutcome(), selfScope);
this.baseEffects.add(baseEffect);
@ -126,18 +120,16 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
}
}
for (Effect effect : baseEffects) {
if (source.activate(game, false)) {
if (effect instanceof ContinuousEffect) {
game.addEffect((ContinuousEffect) effect, source);
} else {
if (spell != null) {
effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility());
}
// Because the permanent is not on the battlefield yet, it has to be taken from the event
effect.setValue(ENTERING_PERMANENT, ((EntersTheBattlefieldEvent) event).getTarget());
effect.apply(game, source);
// if (source.activate(game, false)) { // Why is this needed????
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 false;
}

View file

@ -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.effects.common;
import java.util.ArrayList;
@ -62,17 +61,17 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
this.amount = amount;
this.staticText = setText();
}
public AddManaInAnyCombinationEffect(int amount, String text) {
this(amount);
this.staticText = text;
}
public AddManaInAnyCombinationEffect(int amount, String text, ColoredManaSymbol... coloredManaSymbols) {
this(amount, coloredManaSymbols);
this.staticText = text;
}
public AddManaInAnyCombinationEffect(DynamicValue amount, String text, ColoredManaSymbol... coloredManaSymbols) {
this(amount, coloredManaSymbols);
this.staticText = text;
@ -92,13 +91,13 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null){
if (player != null) {
Mana mana = new Mana();
int amountOfManaLeft = amount.calculate(game, source, this);
while (amountOfManaLeft > 0 && player.canRespond()) {
for (ColoredManaSymbol coloredManaSymbol: manaSymbols) {
int number = player.getAmount(0, amountOfManaLeft, new StringBuilder("How many ").append(coloredManaSymbol.name()).append(" mana?").toString(), game);
for (ColoredManaSymbol coloredManaSymbol : manaSymbols) {
int number = player.getAmount(0, amountOfManaLeft, "How many " + coloredManaSymbol.getColorName() + " mana?", game);
if (number > 0) {
for (int i = 0; i < number; i++) {
mana.add(new Mana(coloredManaSymbol));
@ -111,7 +110,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
}
}
checkToFirePossibleEvents(mana, game, source);
player.getManaPool().addMana(mana, game, source);
player.getManaPool().addMana(mana, game, source);
return true;
}
return false;
@ -130,7 +129,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
sb.append("colors");
} else {
int i = 0;
for (ColoredManaSymbol coloredManaSymbol: manaSymbols) {
for (ColoredManaSymbol coloredManaSymbol : manaSymbols) {
i++;
if (i > 1) {
sb.append(" and/or ");

View file

@ -19,6 +19,7 @@ import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -27,15 +28,13 @@ import mage.target.common.TargetCardInHand;
/**
* Effect for the AmplifyAbility
*
* 702.37. Amplify
* 702.37a Amplify is a static ability. Amplify N means As
* this object enters the battlefield, reveal any number of cards from your hand
* that share a creature type with it. This permanent enters the battlefield
* with N +1/+1 counters on it for each card revealed this way. You cant reveal
* this card or any other cards that are entering the battlefield at the same
* time as this card.
* 702.37b If a creature has multiple instances of amplify,
* each one works separately.
* 702.37. Amplify 702.37a Amplify is a static ability. Amplify N means As
* this object enters the battlefield, reveal any number of cards from your hand
* that share a creature type with it. This permanent enters the battlefield
* with N +1/+1 counters on it for each card revealed this way. You cant reveal
* this card or any other cards that are entering the battlefield at the same
* time as this card. 702.37b If a creature has multiple instances of amplify,
* each one works separately.
*
*
* @author FenrisulfrX
@ -45,6 +44,7 @@ public class AmplifyEffect extends ReplacementEffectImpl {
private final AmplifyFactor amplifyFactor;
public enum AmplifyFactor {
Amplify1("Amplify 1", "put one +1/+1 counters on it", 1),
Amplify2("Amplify 2", "put two +1/+1 counters on it", 2),
Amplify3("Amplify 3", "put three +1/+1 counters on it", 3);
@ -95,7 +95,7 @@ public class AmplifyEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent sourceCreature = game.getPermanent(event.getTargetId());
Permanent sourceCreature = ((EntersTheBattlefieldEvent) event).getTarget();
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && sourceCreature != null) {
FilterCreatureCard filter = new FilterCreatureCard("creatures cards to reveal");
@ -108,7 +108,7 @@ public class AmplifyEffect extends ReplacementEffectImpl {
} else if (filterSubtypes.size() == 1) {
filter.add(filterSubtypes.get(0));
}
if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0){
if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0) {
if (controller.chooseUse(outcome, "Reveal cards to Amplify?", source, game)) {
TargetCardInHand target = new TargetCardInHand(0, Integer.MAX_VALUE, filter);
if (controller.chooseTarget(outcome, target, source, game) && !target.getTargets().isEmpty()) {
@ -128,11 +128,11 @@ public class AmplifyEffect extends ReplacementEffectImpl {
public String getText(Mode mode) {
StringBuilder sb = new StringBuilder(amplifyFactor.toString());
sb.append("<i>(As this enter the battlefield, ");
sb.append(amplifyFactor.getRuleText()).append(" for each card" +
" you reveal that shares a type with it in your hand.)</i>");
sb.append(amplifyFactor.getRuleText()).append(" for each card"
+ " you reveal that shares a type with it in your hand.)</i>");
return sb.toString();
}
@Override
public AmplifyEffect copy() {
return new AmplifyEffect(this);

View file

@ -7,7 +7,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.choices.ChoiceImpl;
import mage.constants.Outcome;
@ -41,7 +40,7 @@ public class ChooseBasicLandTypeEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -29,7 +29,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.choices.ChoiceColor;
import mage.constants.Outcome;
@ -56,7 +55,7 @@ public class ChooseColorEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -29,7 +29,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.repository.CardRepository;
import mage.choices.Choice;
@ -58,7 +57,7 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -7,7 +7,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.repository.CardRepository;
import mage.choices.Choice;
@ -36,7 +35,7 @@ public class ChooseLandTypeEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -7,7 +7,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
@ -41,7 +40,7 @@ public class ChooseOpponentEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -7,7 +7,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
@ -39,7 +38,7 @@ public class ChoosePlayerEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}

View file

@ -33,6 +33,7 @@ import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
@ -54,10 +55,10 @@ public class CopyEffect extends ContinuousEffectImpl {
/**
* Object we copy from
*/
private MageObject copyFromObject;
protected MageObject copyFromObject;
private UUID copyToObjectId;
private ApplyToPermanent applier;
protected UUID copyToObjectId;
protected ApplyToPermanent applier;
public CopyEffect(MageObject copyFromObject, UUID copyToObjectId) {
this(Duration.Custom, copyFromObject, copyToObjectId);
@ -82,14 +83,23 @@ public class CopyEffect extends ContinuousEffectImpl {
if (!(copyFromObject instanceof Permanent) && (copyFromObject instanceof Card)) {
this.copyFromObject = new PermanentCard((Card) copyFromObject, source.getControllerId(), game);
}
Permanent permanent = game.getPermanent(copyToObjectId);
if (permanent != null) {
affectedObjectList.add(new MageObjectReference(permanent, game));
} else if (source.getAbilityType().equals(AbilityType.STATIC)) {
// for replacement effects that let a permanent enter the battlefield as a copy of another permanent we need to apply that copy
// before the permanent is added to the battlefield
permanent = game.getPermanentEntering(copyToObjectId);
if (permanent != null) {
copyToPermanent(permanent, game, source);
// set reference to the permanent later on the battlefield so we have to add already one to the zone change counter
affectedObjectList.add(new MageObjectReference(permanent.getId(), game.getState().getZoneChangeCounter(copyToObjectId) + 1, game));
}
}
}
@Override
public boolean apply(Game game, Ability source) {
if (affectedObjectList.isEmpty()) {
affectedObjectList.add(new MageObjectReference(getSourceId(), game));
}
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
if (permanent == null) {
permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getSourceObjectZoneChangeCounter());
@ -99,6 +109,10 @@ public class CopyEffect extends ContinuousEffectImpl {
return false;
}
}
return copyToPermanent(permanent, game, source);
}
protected boolean copyToPermanent(Permanent permanent, Game game, Ability source) {
permanent.setCopy(true);
permanent.setName(copyFromObject.getName());
permanent.getColor(game).setColor(copyFromObject.getColor(game));

View file

@ -73,7 +73,7 @@ public class CopyPermanentEffect extends OneShotEffect {
this.applier = applier;
this.filter = filter;
this.useTargetOfAbility = useTarget;
this.staticText = "You may have {this} enter the battlefield as a copy of any " + filter.getMessage() + " on the battlefield";
this.staticText = "as a copy of any " + filter.getMessage() + " on the battlefield";
}
public CopyPermanentEffect(final CopyPermanentEffect effect) {
@ -87,7 +87,10 @@ public class CopyPermanentEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
MageObject sourceObject = game.getPermanentEntering(source.getSourceId());
if (sourceObject == null) {
sourceObject = game.getObject(source.getSourceId());
}
if (player != null && sourceObject != null) {
Permanent copyFromPermanent = null;
if (useTargetOfAbility) {

View file

@ -47,30 +47,32 @@ import mage.target.common.TargetControlledCreaturePermanent;
/**
* Effect for the DevourAbility
*
* 702.81. Devour
* 702.81a Devour is a static ability. "Devour N" means "As this object enters the battlefield,
* you may sacrifice any number of creatures. This permanent enters the battlefield with N +1/+1
* counters on it for each creature sacrificed this way."
* 702.81b Some objects have abilities that refer to the number of creatures the permanent devoured.
* "It devoured" means "sacrificed as a result of its devour ability as it entered the battlefield."
*
*
* 702.81. Devour 702.81a Devour is a static ability. "Devour N" means "As this
* object enters the battlefield, you may sacrifice any number of creatures.
* This permanent enters the battlefield with N +1/+1 counters on it for each
* creature sacrificed this way." 702.81b Some objects have abilities that refer
* to the number of creatures the permanent devoured. "It devoured" means
* "sacrificed as a result of its devour ability as it entered the battlefield."
*
*
* @author LevelX2
*/
public class DevourEffect extends ReplacementEffectImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures to devour");
static {
filter.add(new AnotherPredicate());
}
private final DevourFactor devourFactor;
public enum DevourFactor {
Devour1 ("Devour 1", "that many +1/+1 counters on it", 1),
Devour2 ("Devour 2", "twice that many +1/+1 counters on it", 2),
Devour3 ("Devour 3", "three times that many +1/+1 counters on it", 3),
DevourX ("Devour X, where X is the number of creatures devoured this way", "X +1/+1 counters on it for each of those creatures", Integer.MAX_VALUE);
Devour1("Devour 1", "that many +1/+1 counters on it", 1),
Devour2("Devour 2", "twice that many +1/+1 counters on it", 2),
Devour3("Devour 3", "three times that many +1/+1 counters on it", 3),
DevourX("Devour X, where X is the number of creatures devoured this way", "X +1/+1 counters on it for each of those creatures", Integer.MAX_VALUE);
private final String text;
private final String ruleText;
@ -114,9 +116,9 @@ public class DevourEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(source.getSourceId())) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
game.getState().setValue(sourcePermanent.getId().toString() + "devoured", null);
return true;
Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId());
game.getState().setValue(sourcePermanent.getId().toString() + "devoured", null);
return true;
}
return false;
}
@ -128,7 +130,7 @@ public class DevourEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = game.getPermanent(event.getTargetId());
Permanent creature = game.getPermanentEntering(event.getTargetId());
Player controller = game.getPlayer(source.getControllerId());
if (creature != null && controller != null) {
Target target = new TargetControlledCreaturePermanent(1, Integer.MAX_VALUE, filter, true);
@ -141,9 +143,10 @@ public class DevourEffect extends ReplacementEffectImpl {
if (target.getTargets().size() > 0) {
List<ArrayList<String>> cardSubtypes = new ArrayList<>();
int devouredCreatures = target.getTargets().size();
if (!game.isSimulation())
game.informPlayers(new StringBuilder(creature.getName()).append(" devours ").append(devouredCreatures).append(" creatures").toString());
for (UUID targetId: target.getTargets()) {
if (!game.isSimulation()) {
game.informPlayers(creature.getLogName() + " devours " + devouredCreatures + " creatures");
}
for (UUID targetId : target.getTargets()) {
Permanent targetCreature = game.getPermanent(targetId);
if (targetCreature != null) {
cardSubtypes.add((ArrayList<String>) targetCreature.getSubtype());
@ -172,7 +175,7 @@ public class DevourEffect extends ReplacementEffectImpl {
StringBuilder sb = new StringBuilder(devourFactor.toString());
sb.append(" <i>(As this enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with ");
sb.append(devourFactor.getRuleText()).append(")</i>");
return sb.toString();
return sb.toString();
}
public List<ArrayList<String>> getSubtypes(Game game, UUID permanentId) {

View file

@ -34,8 +34,8 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent.EventType;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.StackObject;
import mage.players.Player;
@ -85,7 +85,7 @@ public class DiscardOntoBattlefieldEffect extends ReplacementEffectImpl {
if (card != null) {
Player owner = game.getPlayer(card.getOwnerId());
if (owner != null) {
if (owner.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId())) {
if (owner.moveCards(card, Zone.BATTLEFIELD, source, game)) {
return true;
}
}

View file

@ -31,6 +31,7 @@ import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.AbilityType;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.game.Game;
@ -60,13 +61,15 @@ public class EntersBattlefieldWithXCountersEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) {
permanent = game.getPermanentEntering(source.getSourceId());
}
}
if (permanent != null) {
SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY);
if (spellAbility != null
&& spellAbility.getSourceId().equals(source.getSourceId())
&& permanent.getZoneChangeCounter(game) - 1 == spellAbility.getSourceObjectZoneChangeCounter()) {
&& permanent.getZoneChangeCounter(game) == spellAbility.getSourceObjectZoneChangeCounter()) {
if (spellAbility.getSourceId().equals(source.getSourceId())) { // put into play by normal cast
int amount = spellAbility.getManaCostsToPay().getX();
if (amount > 0) {

View file

@ -66,7 +66,7 @@ public class ExileAndReturnTransformedSourceEffect extends OneShotEffect {
Player owner = game.getPlayer(card.getOwnerId());
if (owner != null) {
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId());
owner.moveCards(card, Zone.BATTLEFIELD, source, game);
if (additionalEffect != null) {
if (additionalEffect instanceof ContinuousEffect) {
game.addEffect((ContinuousEffect) additionalEffect, source);

View file

@ -29,7 +29,6 @@
*/
package mage.abilities.effects.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
@ -128,28 +127,13 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
if (!optional || player.chooseUse(Outcome.DrawCard, getMayText(), source, game)) {
FilterCard pickFilter = filter.copy();
pickFilter.setMessage(getPickText());
TargetCard target = new TargetCard((upTo ? 0 : numberToPick.calculate(game, source, this)), numberToPick.calculate(game, source, this), Zone.PICK, pickFilter);
TargetCard target = new TargetCard((upTo ? 0 : numberToPick.calculate(game, source, this)), numberToPick.calculate(game, source, this), Zone.LIBRARY, pickFilter);
if (player.choose(Outcome.DrawCard, cards, target, game)) {
Cards reveal = new CardsImpl();
for (UUID cardId : target.getTargets()) {
Card card = cards.get(cardId, game);
if (card != null) {
cards.remove(card);
if (targetZoneLookedCards.equals(Zone.BATTLEFIELD)) {
player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId());
} else {
card.moveToZone(targetPickedCards, source.getSourceId(), game, false);
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " moves a card to " + targetPickedCards.toString().toLowerCase());
}
}
if (revealPickedCards) {
reveal.add(card);
}
}
}
Cards pickedCards = new CardsImpl(target.getTargets());
cards.removeAll(pickedCards);
player.moveCards(pickedCards.getCards(game), targetPickedCards, source, game);
if (revealPickedCards) {
player.revealCards(windowName, reveal, game);
player.revealCards(windowName, pickedCards, game);
}
}

View file

@ -29,7 +29,6 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.repository.CardRepository;
import mage.choices.Choice;
@ -72,7 +71,7 @@ public class NameACardEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
MageObject sourceObject = game.getPermanentEntering(source.getSourceId());
if (sourceObject == null) {
game.getObject(source.getSourceId());
}

View file

@ -48,10 +48,9 @@ public class PutPermanentOnBattlefieldEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player;
if(useTargetController) {
if (useTargetController) {
player = game.getPlayer(getTargetPointer().getFirst(game, source));
}
else {
} else {
player = game.getPlayer(source.getControllerId());
}
String choiceText = "Put " + filter.getMessage() + " from your hand onto the battlefield?";
@ -63,23 +62,21 @@ public class PutPermanentOnBattlefieldEffect extends OneShotEffect {
if (player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
player.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId());
return true;
return player.moveCards(card, Zone.BATTLEFIELD, source, game);
}
}
return false;
return true;
}
@Override
public String getText(Mode mode) {
if(this.staticText != null && !this.staticText.isEmpty()) {
if (this.staticText != null && !this.staticText.isEmpty()) {
return staticText;
}
if(useTargetController) {
if (useTargetController) {
return "that player may put " + filter.getMessage() + " from his or her hand onto the battlefield";
}
else {
} else {
return "you may put " + filter.getMessage() + " from your hand onto the battlefield";
}
}

View file

@ -88,8 +88,7 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe
card = game.getCard(getTargetPointer().getFirst(game, source));
}
if (card != null) {
Zone currentZone = game.getState().getZone(card.getId());
controller.putOntoBattlefieldWithInfo(card, game, currentZone, source.getSourceId());
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
}
return true;
}

View file

@ -28,7 +28,6 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
@ -66,7 +65,7 @@ public class TapSourceEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
permanent = game.getPermanentEntering(source.getSourceId());
}
if (permanent != null) {
if (withoutTrigger) {

View file

@ -29,8 +29,8 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.AbilityType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -59,8 +59,8 @@ public class TapSourceUnlessPaysEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) {
permanent = game.getPermanentEntering(source.getSourceId());
}
if (player != null && permanent != null) {
if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game)

View file

@ -30,7 +30,6 @@ package mage.abilities.effects.common.continuous;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Layer;
@ -94,7 +93,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
public void init(Ability source, Game game) {
super.init(source, game);
if (affectedObjectsSet) {
Permanent permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) {
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1, game));
} else {

View file

@ -30,9 +30,9 @@ package mage.abilities.effects.common.counter;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.AbilityType;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.game.Game;
@ -115,8 +115,8 @@ public class AddCountersSourceEffect extends OneShotEffect {
}
} else {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) {
permanent = game.getPermanentEntering(source.getSourceId());
}
if (permanent != null) {
if (counter != null) {

View file

@ -39,13 +39,13 @@ import mage.constants.Zone;
public class AmplifyAbility extends SimpleStaticAbility {
public AmplifyAbility(AmplifyFactor amplifyFactor) {
super(Zone.BATTLEFIELD, new AmplifyEffect(amplifyFactor));
super(Zone.ALL, new AmplifyEffect(amplifyFactor));
}
public AmplifyAbility(final AmplifyAbility ability) {
super(ability);
}
@Override
public AmplifyAbility copy() {
return new AmplifyAbility(this);

View file

@ -104,9 +104,9 @@ class AuraSwapEffect extends OneShotEffect {
if (controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) {
Card auraInHand = game.getCard(target.getFirstTarget());
if (auraInHand != null) {
controller.putOntoBattlefieldWithInfo(auraInHand, game, Zone.HAND, source.getSourceId());
controller.moveCards(auraInHand, Zone.BATTLEFIELD, source, game);
enchantedPermanent.addAttachment(auraInHand.getId(), game);
controller.moveCards(auraPermanent, null, Zone.HAND, source, game);
controller.moveCards(auraPermanent, Zone.HAND, source, game);
return true;
}
}

View file

@ -2,7 +2,6 @@ package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.CounterType;
@ -56,7 +55,7 @@ class BloodthirstEffect extends OneShotEffect {
BloodthirstEffect(int amount) {
super(Outcome.BoostCreature);
this.amount = amount;
staticText = new StringBuilder("this permanent comes into play with ").append(this.amount).append(" +1/+1 counters on it").toString();
staticText = "this permanent comes into play with " + this.amount + " +1/+1 counters on it";
}
BloodthirstEffect(final BloodthirstEffect effect) {
@ -70,10 +69,9 @@ class BloodthirstEffect extends OneShotEffect {
if (player != null) {
BloodthirstWatcher watcher = (BloodthirstWatcher) game.getState().getWatchers().get("DamagedOpponents", source.getControllerId());
if (watcher != null && watcher.conditionMet()) {
Permanent permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(amount), game);
}
}
return true;

View file

@ -76,10 +76,10 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
this.addDashCost(manaString);
Ability ability = new EntersBattlefieldAbility(
new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false),
DashedCondition.getInstance(), false, "", "");
DashedCondition.getInstance(), "", "");
ability.addEffect(new DashAddDelayedTriggeredAbilityEffect());
addSubAbility(ability);
}
public DashAbility(final DashAbility ability) {
@ -226,16 +226,18 @@ class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return the dashed creature from the battlefield to its owner's hand");
effect.setTargetPointer(new FixedTarget(source.getSourceId()));
// init target pointer now because the dashed creature will only be returned from current zone
effect.getTargetPointer().init(game, source);
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
game.addDelayedTriggeredAbility(delayedAbility);
if (game.getPermanentEntering(source.getSourceId()) != null) {
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return the dashed creature from the battlefield to its owner's hand");
// init target pointer now because the dashed creature will only be returned from battlefield zone (now in entering state so zone change counter is not raised yet)
effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
game.addDelayedTriggeredAbility(delayedAbility);
return true;
}
return false;
}
}

View file

@ -27,51 +27,51 @@
*/
package mage.abilities.keyword;
import mage.constants.Zone;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.DevourEffect;
import mage.abilities.effects.common.DevourEffect.DevourFactor;
import mage.constants.Zone;
/**
* 502.82. Devour
*
* 502.82a Devour is a static ability. "Devour N" means "As this object comes into play,
* you may sacrifice any number of creatures. This permanent comes into play with N +1/+1
* counters on it for each creature sacrificed this way."
* 502.82a Devour is a static ability. "Devour N" means "As this object comes
* into play, you may sacrifice any number of creatures. This permanent comes
* into play with N +1/+1 counters on it for each creature sacrificed this way."
*
* 502.82b Some objects have abilities that refer to the number of creatures the permanent
* devoured. "It devoured" means "sacrificed as a result of its devour ability as it came
* into play."
* 502.82b Some objects have abilities that refer to the number of creatures the
* permanent devoured. "It devoured" means "sacrificed as a result of its devour
* ability as it came into play."
*
* Devour appears only on creature cards.
*
* A creature with devour can devour other creatures no matter how it comes into play.
* A creature with devour can devour other creatures no matter how it comes into
* play.
*
* You may choose to not sacrifice any creatures.
*
* If you play a creature with devour as a spell, you choose how many and which creatures
* to devour as part of the resolution of that spell. (It can't be countered at this point.)
* The same is true of a spell or ability that lets you put a creature with devour into play.
* If you play a creature with devour as a spell, you choose how many and which
* creatures to devour as part of the resolution of that spell. (It can't be
* countered at this point.) The same is true of a spell or ability that lets
* you put a creature with devour into play.
*
* You may sacrifice only creatures that are already in play. If a creature with devour and
* another creature are coming into play under your control at the same time, the creature
* with devour can't devour that other creature. The creature with devour also can't devour
* itself.
* You may sacrifice only creatures that are already in play. If a creature with
* devour and another creature are coming into play under your control at the
* same time, the creature with devour can't devour that other creature. The
* creature with devour also can't devour itself.
*
* If multiple creatures with devour are coming into play under your control at the same time,
* you may use each one's devour ability. A creature you already control can be devoured by
* only one of them, however. (In other words, you can't sacrifice the same creature to satisfy
* multiple devour abilities.) All creatures devoured this way are sacrificed at the same time.
* If multiple creatures with devour are coming into play under your control at
* the same time, you may use each one's devour ability. A creature you already
* control can be devoured by only one of them, however. (In other words, you
* can't sacrifice the same creature to satisfy multiple devour abilities.) All
* creatures devoured this way are sacrificed at the same time.
*
* @author LevelX2
*/
public class DevourAbility extends SimpleStaticAbility {
public class DevourAbility extends SimpleStaticAbility {
public DevourAbility(DevourFactor devourFactor) {
super(Zone.BATTLEFIELD, new DevourEffect(devourFactor));
public DevourAbility(DevourFactor devourFactor) {
super(Zone.ALL, new DevourEffect(devourFactor));
}
public DevourAbility(final DevourAbility ability) {
@ -82,4 +82,4 @@ import mage.abilities.effects.common.DevourEffect.DevourFactor;
public DevourAbility copy() {
return new DevourAbility(this);
}
}
}

View file

@ -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.keyword;
import mage.abilities.Ability;
@ -44,46 +43,53 @@ import mage.target.targetpointer.FixedTarget;
* FAQ 2013/01/11
*
* 702.98. Evolve
*
* 702.98a Evolve is a triggered ability. "Evolve" means "Whenever a creature enters
* the battlefield under your control, if that creature's power is greater than this
* creature's power and/or that creature's toughness is greater than this creature's
* toughness, put a +1/+1 counter on this creature."
*
* 702.98b If a creature has multiple instances of evolve, each triggers separately
*
* 702.98a Evolve is a triggered ability. "Evolve" means "Whenever a creature
* enters the battlefield under your control, if that creature's power is
* greater than this creature's power and/or that creature's toughness is
* greater than this creature's toughness, put a +1/+1 counter on this
* creature."
*
* 702.98b If a creature has multiple instances of evolve, each triggers
* separately
*
* Rulings
*
* When comparing the stats of the two creatures, you always compare power to power and toughness to toughness.
* Whenever a creature enters the battlefield under your control, check its power and toughness against
* the power and toughness of the creature with evolve. If neither stat of the new creature is greater,
* evolve won't trigger at all. For example, if you control a 2/3 creature with evolve and a 2/2 creature
* enters the battlefield under your control, you won't have the opportunity to cast a spell like Giant Growth
* to make the 2/2 creature large enough to cause evolve to trigger.
* If evolve triggers, the stat comparison will happen again when the ability tries to resolve. If
* neither stat of the new creature is greater, the ability will do nothing. If the creature that
* entered the battlefield leaves the battlefield before evolve tries to resolve, use its last known
* power and toughness to compare the stats.
* If a creature enters the battlefield with +1/+1 counters on it, consider those counters when determining
* if evolve will trigger. For example, a 1/1 creature that enters the battlefield with two +1/+1 counters
* on it will cause the evolve ability of a 2/2 creature to trigger.
* If multiple creatures enter the battlefield at the same time, evolve may trigger multiple times, although the stat
* comparison will take place each time one of those abilities tries to resolve. For example, if you control a 2/2
* creature with evolve and two 3/3 creatures enter the battlefield, evolve will trigger twice. The first ability
* will resolve and put a +1/+1 counter on the creature with evolve. When the second ability tries to resolve,
* neither the power nor the toughness of the new creature is greater than that of the creature with evolve,
* so that ability does nothing.
* When comparing the stats as the evolve ability resolves, it's possible that the stat that's greater changes
* from power to toughness or vice versa. If this happens, the ability will still resolve and you'll put a +1/+1
* counter on the creature with evolve. For example, if you control a 2/2 creature with evolve and a 1/3 creature
* enters the battlefield under your control, it toughness is greater so evolve will trigger. In response, the 1/3
* creature gets +2/-2. When the evolve trigger tries to resolve, its power is greater. You'll put a +1/+1
* counter on the creature with evolve.
*
*
* When comparing the stats of the two creatures, you always compare power to
* power and toughness to toughness. Whenever a creature enters the battlefield
* under your control, check its power and toughness against the power and
* toughness of the creature with evolve. If neither stat of the new creature is
* greater, evolve won't trigger at all. For example, if you control a 2/3
* creature with evolve and a 2/2 creature enters the battlefield under your
* control, you won't have the opportunity to cast a spell like Giant Growth to
* make the 2/2 creature large enough to cause evolve to trigger. If evolve
* triggers, the stat comparison will happen again when the ability tries to
* resolve. If neither stat of the new creature is greater, the ability will do
* nothing. If the creature that entered the battlefield leaves the battlefield
* before evolve tries to resolve, use its last known power and toughness to
* compare the stats. If a creature enters the battlefield with +1/+1 counters
* on it, consider those counters when determining if evolve will trigger. For
* example, a 1/1 creature that enters the battlefield with two +1/+1 counters
* on it will cause the evolve ability of a 2/2 creature to trigger. If multiple
* creatures enter the battlefield at the same time, evolve may trigger multiple
* times, although the stat comparison will take place each time one of those
* abilities tries to resolve. For example, if you control a 2/2 creature with
* evolve and two 3/3 creatures enter the battlefield, evolve will trigger
* twice. The first ability will resolve and put a +1/+1 counter on the creature
* with evolve. When the second ability tries to resolve, neither the power nor
* the toughness of the new creature is greater than that of the creature with
* evolve, so that ability does nothing. When comparing the stats as the evolve
* ability resolves, it's possible that the stat that's greater changes from
* power to toughness or vice versa. If this happens, the ability will still
* resolve and you'll put a +1/+1 counter on the creature with evolve. For
* example, if you control a 2/2 creature with evolve and a 1/3 creature enters
* the battlefield under your control, it toughness is greater so evolve will
* trigger. In response, the 1/3 creature gets +2/-2. When the evolve trigger
* tries to resolve, its power is greater. You'll put a +1/+1 counter on the
* creature with evolve.
*
* @author LevelX2
*/
public class EvolveAbility extends TriggeredAbilityImpl {
public EvolveAbility() {
@ -169,4 +175,3 @@ class EvolveEffect extends OneShotEffect {
return false;
}
}

View file

@ -1,30 +1,30 @@
/*
* 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.
*/
* 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.keyword;
import java.util.Locale;
@ -48,18 +48,19 @@ import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* 702.56. Graft
* 702.56a. Graft represents both a static ability and a triggered ability. Graft N means,
* "This permanent enters the battlefield with N +1/+1 counters on it" and, "Whenever
* another creature enters the battlefield, if this permanent has a +1/+1 counter on it,
* you may move a +1/+1 counter from this permanent onto that creature."
* 702.56. Graft 702.56a. Graft represents both a static ability and a triggered
* ability. Graft N means, "This permanent enters the battlefield with N +1/+1
* counters on it" and, "Whenever another creature enters the battlefield, if
* this permanent has a +1/+1 counter on it, you may move a +1/+1 counter from
* this permanent onto that creature."
*
* 702.56b. If a creature has multiple instances of graft, each one works separately.
* 702.56b. If a creature has multiple instances of graft, each one works
* separately.
*
* @author LevelX2
*/
public class GraftAbility extends TriggeredAbilityImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
private int amount;
@ -112,10 +113,10 @@ public class GraftAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("Graft");
sb.append(" ").append(amount).append(" <i>(This ").append(cardtype).append(" enters the battlefield with ")
.append(amount == 1 ? "a": CardUtil.numberToText(amount))
.append(" +1/+1 counter on it. Whenever a creature enters the battlefield, you may move a +1/+1 counter from this ")
.append(cardtype).append(" onto it.)</i>");
sb.append(" ").append(amount).append(" <i>(This ").append(cardtype).append(" enters the battlefield with ")
.append(amount == 1 ? "a" : CardUtil.numberToText(amount))
.append(" +1/+1 counter on it. Whenever a creature enters the battlefield, you may move a +1/+1 counter from this ")
.append(cardtype).append(" onto it.)</i>");
return sb.toString();
}
@ -126,7 +127,7 @@ class GraftStaticAbility extends StaticAbility {
private String ruleText;
public GraftStaticAbility(int amount) {
super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount))));
super(Zone.ALL, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount))));
ruleText = new StringBuilder("This enters the battlefield with ").append(amount).append(" +1/+1 counter on it.").toString();
this.setRuleVisible(false);
}
@ -147,7 +148,6 @@ class GraftStaticAbility extends StaticAbility {
}
}
class GraftDistributeCounterEffect extends OneShotEffect {
public GraftDistributeCounterEffect() {

View file

@ -65,7 +65,7 @@ import mage.util.CardUtil;
public class HideawayAbility extends StaticAbility {
public HideawayAbility() {
super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new TapSourceEffect(true)));
super(Zone.ALL, new EntersBattlefieldEffect(new TapSourceEffect(true)));
Ability ability = new EntersBattlefieldTriggeredAbility(new HideawayExileEffect(), false);
ability.setRuleVisible(false);
addSubAbility(ability);
@ -115,17 +115,17 @@ class HideawayExileEffect extends OneShotEffect {
if (hideawaySource == null || controller == null) {
return false;
}
Cards cards = new CardsImpl(Zone.LIBRARY);
cards.addAll(controller.getLibrary().getTopCards(game, 4));
cards.addAll(controller.getLibrary().getTopCards(game, 4));
if (cards.size() > 0) {
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
if (controller.choose(Outcome.Detriment, cards, target1, game)) {
Card card = cards.get(target1.getFirstTarget(), game);
if (card != null) {
cards.remove(card);
controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source),
"Hideaway (" + hideawaySource.getIdName() +")", source.getSourceId(), game, Zone.LIBRARY, false);
controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source),
"Hideaway (" + hideawaySource.getIdName() + ")", source.getSourceId(), game, Zone.LIBRARY, false);
card.setFaceDown(true, game);
}
}
@ -159,7 +159,7 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl {
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (game.getState().getZone(objectId) != Zone.EXILED
if (game.getState().getZone(objectId) != Zone.EXILED
|| !game.getState().getCardState(objectId).isFaceDown()) {
return false;
}
@ -180,4 +180,3 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl {
return false;
}
}

View file

@ -203,7 +203,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
if (zcc == 0) {
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
}
if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED) || source.getAbilityType().equals(AbilityType.STATIC))) {
if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED))) {
--zcc;
}
return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText : "");

View file

@ -108,7 +108,7 @@ class ModularStaticAbility extends StaticAbility {
private String ruleText;
public ModularStaticAbility(int amount) {
super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount))));
super(Zone.ALL, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount))));
ruleText = "This enters the battlefield with " + CardUtil.numberToText(amount, "a") + " +1/+1 counter" + (amount != 1 ? "s" : "") + " on it.";
this.setRuleVisible(false);
}

View file

@ -31,7 +31,6 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.SunburstCount;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.CardType;
@ -89,7 +88,7 @@ class SunburstEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) {
Counter counter;
if (permanent.getCardType().contains(CardType.CREATURE)) {

View file

@ -30,7 +30,6 @@ package mage.abilities.keyword;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.constants.Outcome;
@ -51,7 +50,7 @@ public class TributeAbility extends EntersBattlefieldAbility {
private int tributeValue;
public TributeAbility(int tributeValue) {
super(new TributeEffect(tributeValue), false);
super(new TributeEffect(tributeValue));
this.tributeValue = tributeValue;
}
@ -98,7 +97,7 @@ class TributeEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = (Permanent) getValue(EntersBattlefieldEffect.ENTERING_PERMANENT);
Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId());
if (controller != null && sourcePermanent != null) {
UUID opponentId;
if (game.getOpponents(controller.getId()).size() == 1) {

View file

@ -26,14 +26,14 @@
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.RestrictionEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
@ -41,49 +41,41 @@ import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
//
// 702.96. Unleash
//
// 702.96a Unleash is a keyword that represents two static abilities.
// 702.96a Unleash is a keyword that represents two static abilities.
// "Unleash" means "You may have this permanent enter the battlefield with an additional +1/+1 counter on it"
// and "This permanent cant block as long as it has a +1/+1 counter on it."
public class UnleashAbility extends SimpleStaticAbility {
public class UnleashAbility extends SimpleStaticAbility {
public UnleashAbility() {
super(Zone.ALL, new UnleashReplacementEffect());
this.addEffect(new UnleashRestrictionEffect());
}
public UnleashAbility(final UnleashAbility ability) {
super(ability);
}
@Override
public UnleashAbility copy() {
return new UnleashAbility(this);
}
@Override
public String getRule() {
return "Unleash <i>(You may have this creature enter the battlefield with a +1/+1 counter on it. It can't block as long as it has a +1/+1 counter on it.)</i>";
}
}
class UnleashReplacementEffect extends ReplacementEffectImpl {
public UnleashReplacementEffect() {
super(Duration.EndOfGame, Outcome.Detriment);
}
public UnleashReplacementEffect(UnleashReplacementEffect effect) {
super(effect);
}
@ -95,53 +87,51 @@ class UnleashReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(source.getSourceId())) {
return true;
}
return false;
return event.getTargetId().equals(source.getSourceId());
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = game.getPermanent(event.getTargetId());
Permanent creature = game.getPermanentEntering(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (creature != null && controller != null) {
if (controller.chooseUse(outcome, "Unleash "+ creature.getName() +"?", source, game)) {
if (!game.isSimulation())
if (controller.chooseUse(outcome, "Unleash " + creature.getName() + "?", source, game)) {
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " unleashes " + creature.getName());
}
creature.addCounters(CounterType.P1P1.createInstance(), game);
}
}
return false;
}
@Override
public String getText(Mode mode) {
return staticText;
}
@Override
public UnleashReplacementEffect copy() {
return new UnleashReplacementEffect(this);
}
}
class UnleashRestrictionEffect extends RestrictionEffect {
public UnleashRestrictionEffect() {
super(Duration.WhileOnBattlefield);
}
public UnleashRestrictionEffect(final UnleashRestrictionEffect effect) {
super(effect);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent != null && permanent.getId().equals(source.getSourceId())) {
@ -151,14 +141,14 @@ class UnleashRestrictionEffect extends RestrictionEffect {
}
return false;
}
@Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
return false;
}
@Override
public UnleashRestrictionEffect copy() {
return new UnleashRestrictionEffect(this);
}
}
}

View file

@ -512,7 +512,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
}
break;
case STACK:
StackObject stackObject = game.getStack().getSpell(getSpellAbility().getId());
StackObject stackObject;
if (getSpellAbility() != null) {
stackObject = game.getStack().getSpell(getSpellAbility().getId());
} else {
stackObject = game.getStack().getSpell(this.getId());
}
if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId());
if (stackObject == null) {

View file

@ -115,6 +115,10 @@ public interface Game extends MageItem, Serializable {
Permanent getPermanentOrLKIBattlefield(UUID permanentId);
Permanent getPermanentEntering(UUID permanentId);
Map<UUID, Permanent> getPermanentsEntering();
Map<Zone, HashMap<UUID, MageObject>> getLKI();
Card getCard(UUID cardId);

View file

@ -176,6 +176,9 @@ public abstract class GameImpl implements Game, Serializable {
// Used to check if an object was moved by the current effect in resolution (so Wrath like effect can be handled correctly)
protected Map<Zone, Set<UUID>> shortLivingLKI = new EnumMap<>(Zone.class);
// Permanents entering the Battlefield while handling replacement effects before they are added to the battlefield
protected Map<UUID, Permanent> permanentsEntering = new HashMap<>();
protected GameState state;
private transient Stack<Integer> savedStates = new Stack<>();
protected transient GameStates gameStates = new GameStates();
@ -244,6 +247,7 @@ public abstract class GameImpl implements Game, Serializable {
this.lki.putAll(game.lki);
this.lkiExtended.putAll(game.lkiExtended);
this.shortLivingLKI.putAll(game.shortLivingLKI);
this.permanentsEntering.putAll(game.permanentsEntering);
if (logger.isDebugEnabled()) {
copyCount++;
copyTime += (System.currentTimeMillis() - t1);
@ -501,6 +505,16 @@ public abstract class GameImpl implements Game, Serializable {
return permanent;
}
@Override
public Permanent getPermanentEntering(UUID permanentId) {
return permanentsEntering.get(permanentId);
}
@Override
public Map<UUID, Permanent> getPermanentsEntering() {
return permanentsEntering;
}
@Override
public Card getCard(UUID cardId) {
if (cardId == null) {
@ -891,7 +905,7 @@ public abstract class GameImpl implements Game, Serializable {
return;
}
getState().setChoosingPlayerId(choosingPlayerId); // needed to start/stop the timer if active
if (choosingPlayer != null && choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) {
if (choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) {
startingPlayerId = targetPlayer.getTargets().get(0);
} else if (getState().getPlayers().size() < 3) {
// not possible to choose starting player, choosing player has probably conceded, so stop here
@ -2539,8 +2553,10 @@ public abstract class GameImpl implements Game, Serializable {
card.setZone(Zone.BATTLEFIELD, this);
card.setOwnerId(ownerId);
PermanentCard permanent = new PermanentCard(card.getCard(), ownerId, this);
getBattlefield().addPermanent(permanent);
getPermanentsEntering().put(permanent.getId(), permanent);
permanent.entersBattlefield(permanent.getId(), this, Zone.OUTSIDE, false);
getBattlefield().addPermanent(permanent);
getPermanentsEntering().remove(permanent.getId());
((PermanentImpl) permanent).removeSummoningSickness();
if (card.isTapped()) {
permanent.setTapped(true);

View file

@ -647,6 +647,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ATTACH, objectId, permanentId, controllerId))) {
this.attachments.add(permanentId);
Permanent attachment = game.getPermanent(permanentId);
if (attachment == null) {
attachment = game.getPermanentEntering(permanentId);
}
if (attachment != null) {
attachment.attachTo(objectId, game);
game.fireEvent(new GameEvent(GameEvent.EventType.ATTACHED, objectId, permanentId, controllerId));

View file

@ -42,6 +42,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
@ -148,31 +149,43 @@ public class Token extends MageObjectImpl {
GameEvent event = new GameEvent(EventType.CREATE_TOKEN, null, sourceId, controllerId, amount, this.getCardType().contains(CardType.CREATURE));
if (!game.replaceEvent(event)) {
amount = event.getAmount();
List<Permanent> permanents = new ArrayList<>();
List<Permanent> permanentsEntered = new ArrayList<>();
for (int i = 0; i < amount; i++) {
PermanentToken newToken = new PermanentToken(this, event.getPlayerId(), setCode, game); // use event.getPlayerId() because it can be replaced by replacement effect
game.getState().addCard(newToken);
game.addPermanent(newToken);
if (tapped) {
newToken.setTapped(true);
}
this.lastAddedTokenIds.add(newToken.getId());
this.lastAddedTokenId = newToken.getId();
game.setScopeRelevant(true);
game.applyEffects();
boolean entered = newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true);
game.setScopeRelevant(false);
game.applyEffects();
if (entered) {
game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD));
if (attacking && game.getCombat() != null) {
game.getCombat().addAttackingCreature(newToken.getId(), game);
}
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield");
}
permanents.add(newToken);
game.getPermanentsEntering().put(newToken.getId(), newToken);
newToken.setTapped(tapped);
}
game.setScopeRelevant(true);
for (Permanent permanent : permanents) {
if (permanent.entersBattlefield(sourceId, game, Zone.OUTSIDE, true)) {
permanentsEntered.add(permanent);
} else {
game.getPermanentsEntering().remove(permanent.getId());
}
}
game.setScopeRelevant(false);
for (Permanent permanent : permanentsEntered) {
game.addPermanent(permanent);
permanent.setZone(Zone.BATTLEFIELD, game);
game.getPermanentsEntering().remove(permanent.getId());
this.lastAddedTokenIds.add(permanent.getId());
this.lastAddedTokenId = permanent.getId();
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD));
if (attacking && game.getCombat() != null) {
game.getCombat().addAttackingCreature(permanent.getId(), game);
}
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " puts a " + permanent.getLogName() + " token onto the battlefield");
}
}
game.applyEffects();
return true;
}
return false;

View file

@ -232,7 +232,7 @@ public class Spell extends StackObjImpl implements Card {
card.getCardType().remove(CardType.CREATURE);
card.getSubtype().add("Aura");
}
if (card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId)) {
if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) {
if (bestow) {
// card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed
// TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities
@ -253,8 +253,7 @@ public class Spell extends StackObjImpl implements Card {
// Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature
if (this.getSpellAbility() instanceof BestowAbility) {
updateOptionalCosts(0);
result = card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId);
return result;
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
} else {
//20091005 - 608.2b
if (!game.isSimulation()) {
@ -265,9 +264,7 @@ public class Spell extends StackObjImpl implements Card {
}
} else {
updateOptionalCosts(0);
// return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
result = card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId, false, faceDown);
return result;
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
}
}

View file

@ -629,15 +629,26 @@ public interface Player extends MageItem, Copyable<Player> {
* @param game
* @return
*/
@Deprecated
boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game);
@Deprecated
boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game);
@Deprecated
boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game);
boolean moveCards(Card card, Zone toZone, Ability source, Game game);
boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects);
boolean moveCards(Cards cards, Zone toZone, Ability source, Game game);
boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game);
/**
* Iniversal method to move cards from one zone to another. Do not mix
* objects from different from zones to move.
*
* @param cards
* @param toZone
@ -740,6 +751,7 @@ public interface Player extends MageItem, Copyable<Player> {
* @param sourceId
* @return
*/
@Deprecated
boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId);
/**

View file

@ -773,6 +773,9 @@ public abstract class PlayerImpl implements Player, Serializable {
public boolean addAttachment(UUID permanentId, Game game) {
if (!this.attachments.contains(permanentId)) {
Permanent aura = game.getPermanent(permanentId);
if (aura == null) {
aura = game.getPermanentEntering(permanentId);
}
if (aura != null) {
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ENCHANT_PLAYER, playerId, permanentId, aura.getControllerId()))) {
this.attachments.add(permanentId);
@ -1016,8 +1019,7 @@ public abstract class PlayerImpl implements Player, Serializable {
//20091005 - 305.1
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) {
// int bookmark = game.bookmarkState();
Zone zone = game.getState().getZone(card.getId());
if (card.putOntoBattlefield(game, zone, null, playerId)) {
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
landsPlayed++;
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId));
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
@ -2980,40 +2982,12 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game) {
if (cards.isEmpty()) {
return true;
}
Set<Card> successfulMovedCards = new LinkedHashSet<>();
switch (toZone) {
case EXILED:
for (Card card : cards) {
fromZone = game.getState().getZone(card.getId());
boolean withName = (fromZone.equals(Zone.BATTLEFIELD) || fromZone.equals(Zone.STACK)) || !card.isFaceDown(game);
if (moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, withName)) {
successfulMovedCards.add(card);
}
}
break;
case GRAVEYARD:
successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
break;
case HAND:
case BATTLEFIELD:
return moveCards(cards, toZone, source, game, false, false, false, null);
case LIBRARY:
for (Card card : cards) {
fromZone = game.getState().getZone(card.getId());
boolean hideCard = fromZone.equals(Zone.HAND) || fromZone.equals(Zone.LIBRARY);
if (moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, !hideCard)) {
successfulMovedCards.add(card);
}
}
break;
default:
throw new UnsupportedOperationException("to Zone not supported yet");
}
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
return successfulMovedCards.size() > 0;
return moveCards(cards, toZone, source, game, false, false, false, null);
}
@Override
public boolean moveCards(Card card, Zone toZone, Ability source, Game game) {
return moveCards(card, toZone, source, game, false, false, false, null);
}
@Override
@ -3025,6 +2999,16 @@ public abstract class PlayerImpl implements Player, Serializable {
return moveCards(cardList, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@Override
public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) {
return moveCards(cards.getCards(game), toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game) {
return moveCards(cards, toZone, source, game, false, false, false, null);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects) {
if (cards.isEmpty()) {
@ -3033,7 +3017,11 @@ public abstract class PlayerImpl implements Player, Serializable {
Set<Card> successfulMovedCards = new LinkedHashSet<>();
Zone fromZone = null;
switch (toZone) {
case BATTLEFIELD:
case GRAVEYARD:
fromZone = game.getState().getZone(cards.iterator().next().getId());
successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
break;
case BATTLEFIELD: // new logic that does not yet add the permanents to battlefield while replacement effects are handled
List<Permanent> permanents = new ArrayList<>();
List<Permanent> permanentsEntered = new ArrayList<>();
for (Card card : cards) {
@ -3047,6 +3035,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// get permanent
Permanent permanent = new PermanentCard(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now
permanents.add(permanent);
game.getPermanentsEntering().put(permanent.getId(), permanent);
card.checkForCountersToAdd(permanent, game);
permanent.setTapped(tapped);
permanent.setFaceDown(faceDown, game);
@ -3060,6 +3049,8 @@ public abstract class PlayerImpl implements Player, Serializable {
fromZone = game.getState().getZone(permanent.getId());
if (permanent.entersBattlefield(source.getSourceId(), game, fromZone, true)) {
permanentsEntered.add(permanent);
} else {
game.getPermanentsEntering().remove(permanent.getId());
}
}
game.setScopeRelevant(false);
@ -3071,11 +3062,16 @@ public abstract class PlayerImpl implements Player, Serializable {
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
game.addPermanent(permanent);
permanent.setZone(Zone.BATTLEFIELD, game);
// check if there are counters to add to the permanent (e.g. from non replacement effects like Persist)
game.getPermanentsEntering().remove(permanent.getId());
game.setScopeRelevant(true);
successfulMovedCards.add(permanent);
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), fromZone, Zone.BATTLEFIELD));
if (!game.isSimulation()) {
game.informPlayers(this.getLogName() + " puts " + (faceDown ? "a card face down " : permanent.getLogName())
+ " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield");
}
} else {
game.getPermanentsEntering().remove(permanent.getId());
}
}
game.applyEffects();
@ -3090,8 +3086,26 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
break;
case EXILED:
for (Card card : cards) {
fromZone = game.getState().getZone(card.getId());
boolean withName = (fromZone.equals(Zone.BATTLEFIELD) || fromZone.equals(Zone.STACK)) || !card.isFaceDown(game);
if (moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, withName)) {
successfulMovedCards.add(card);
}
}
break;
case LIBRARY:
for (Card card : cards) {
fromZone = game.getState().getZone(card.getId());
boolean hideCard = fromZone.equals(Zone.HAND) || fromZone.equals(Zone.LIBRARY);
if (moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, !hideCard)) {
successfulMovedCards.add(card);
}
}
break;
default:
throw new UnsupportedOperationException("to Zone not supported yet");
throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet");
}
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
@ -3293,16 +3307,19 @@ public abstract class PlayerImpl implements Player, Serializable {
return result;
}
@Deprecated
@Override
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId) {
return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, false, false);
}
@Deprecated
@Override
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) {
return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, false);
}
@Deprecated
@Override
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) {
boolean result = false;

View file

@ -0,0 +1,40 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.util.functions;
import mage.MageObject;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class AddSubtypeApplier extends ApplyToPermanent {
private final String subtype;
public AddSubtypeApplier(String subtype) {
this.subtype = subtype;
}
@Override
public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getSubtype().contains(subtype)) {
permanent.getSubtype().add(subtype);
}
return true;
}
@Override
public Boolean apply(Game game, MageObject mageObject) {
if (!mageObject.getSubtype().contains(subtype)) {
mageObject.getSubtype().add(subtype);
}
return true;
}
}

View file

@ -27,10 +27,36 @@
*/
package mage.util.functions;
import mage.MageObject;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CardTypeApplier {
public class CardTypeApplier extends ApplyToPermanent {
private final CardType cardType;
public CardTypeApplier(CardType cardType) {
this.cardType = cardType;
}
@Override
public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getCardType().contains(cardType)) {
permanent.getCardType().add(cardType);
}
return true;
}
@Override
public Boolean apply(Game game, MageObject mageObject) {
if (!mageObject.getCardType().contains(cardType)) {
mageObject.getCardType().add(cardType);
}
return true;
}
}