* Fixed a problem with replacement effects that add counters were applied more than once to the same EnteresTheBattlefield event (fixes #2872).

This commit is contained in:
LevelX2 2017-04-01 17:43:17 +02:00
parent 94f77675ec
commit d0bf77cedf
25 changed files with 302 additions and 104 deletions

View file

@ -27,6 +27,8 @@
*/
package mage.cards.a;
import java.util.ArrayList;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility;
@ -45,8 +47,6 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
import java.util.UUID;
/**
*
* @author jeffwadsworth
@ -54,7 +54,7 @@ import java.util.UUID;
public class ArsenalThresher extends CardImpl {
public ArsenalThresher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{W/B}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{W/B}{U}");
this.subtype.add("Construct");
this.power = new MageInt(2);
@ -109,7 +109,8 @@ class ArsenalThresherEffect extends OneShotEffect {
}
if (arsenalThresher != null) {
controller.revealCards(arsenalThresher.getIdName(), cards, game);
arsenalThresher.addCounters(CounterType.P1P1.createInstance(cards.size()), source, game);
ArrayList<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects"); // the basic event is the EntersBattlefieldEvent, so use already applied replacement effects from that event
arsenalThresher.addCounters(CounterType.P1P1.createInstance(cards.size()), source, game, appliedEffects);
}
}
}

View file

@ -27,6 +27,7 @@
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -47,8 +48,6 @@ import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author emerald000
@ -62,7 +61,7 @@ public class BramblewoodParagon extends CardImpl {
}
public BramblewoodParagon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add("Elf");
this.subtype.add("Warrior");
this.power = new MageInt(2);
@ -120,7 +119,7 @@ class BramblewoodParagonReplacementEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source, game);
creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
return false;
}

View file

@ -27,6 +27,9 @@
*/
package mage.cards.c;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
@ -45,10 +48,6 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
*
* @author jeffwadsworth
@ -57,7 +56,7 @@ import java.util.UUID;
public class ChorusOfTheConclave extends CardImpl {
public ChorusOfTheConclave(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}{W}{W}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add("Dryad");
@ -198,7 +197,7 @@ class ChorusOfTheConclaveReplacementEffect2 extends ReplacementEffectImpl {
String key = event.getSourceId().toString() + (game.getState().getZoneChangeCounter(event.getSourceId()) - 1);
int xValue = spellX.get(key);
if (xValue > 0) {
creature.addCounters(CounterType.P1P1.createInstance(xValue), source, game);
creature.addCounters(CounterType.P1P1.createInstance(xValue), source, game, event.getAppliedEffects());
game.informPlayers(sourceObject.getLogName() + ": " + creature.getLogName() + " enters the battlefield with " + xValue + " +1/+1 counter" + (xValue > 1 ? "s" : "") + " on it");
}
spellX.remove(key);

View file

@ -27,6 +27,7 @@
*/
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -45,15 +46,13 @@ import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author nantuko
*/
public class DearlyDeparted extends CardImpl {
public DearlyDeparted(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}");
this.subtype.add("Spirit");
this.power = new MageInt(5);
@ -104,7 +103,7 @@ class DearlyDepartedEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent target = ((EntersTheBattlefieldEvent) event).getTarget();
if (target != null) {
target.addCounters(CounterType.P1P1.createInstance(), source, game);
target.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
return false;
}

View file

@ -58,7 +58,7 @@ import mage.target.common.TargetCreaturePermanent;
public class LongRoadHome extends CardImpl {
public LongRoadHome(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
// Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.
this.getSpellAbility().addEffect(new LongRoadHomeEffect());
@ -163,7 +163,7 @@ class LongRoadHomeEntersBattlefieldEffect extends ReplacementEffectImpl {
public LongRoadHomeEntersBattlefieldEffect(MageObjectReference objectToReturn) {
super(Duration.Custom, Outcome.BoostCreature);
this.objectToReturn = objectToReturn;
staticText = "that card returns to the battlefield with a +1/+1 counter on it";
staticText = "that card to the battlefield under its owner's control with a +1/+1 counter on it";
}
public LongRoadHomeEntersBattlefieldEffect(LongRoadHomeEntersBattlefieldEffect effect) {
@ -188,7 +188,7 @@ class LongRoadHomeEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
permanent.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
discard(); // use only once
}
return false;

View file

@ -30,17 +30,19 @@ package mage.cards.n;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.common.SpellMasteryCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCardInGraveyard;
@ -51,16 +53,15 @@ import mage.target.common.TargetCardInGraveyard;
public class NecromanticSummons extends CardImpl {
public NecromanticSummons(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}");
this.getSpellAbility().addEffect(new NecromanticSummoningReplacementEffect());// has to be added before the moving effect
// Put target creature card from a graveyard onto the battlefield under your control.
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
// <i>Spell mastery</i> - If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it.
Effect effect = new ConditionalOneShotEffect(new NecromanticSummoningEffect(),
SpellMasteryCondition.instance, "<br><i>Spell mastery</i> - If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new InfoEffect("\"<br><i>Spell mastery</i> - If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it\""));
}
public NecromanticSummons(final NecromanticSummons card) {
@ -73,29 +74,46 @@ public class NecromanticSummons extends CardImpl {
}
}
class NecromanticSummoningEffect extends OneShotEffect {
class NecromanticSummoningReplacementEffect extends ReplacementEffectImpl {
public NecromanticSummoningEffect() {
super(Outcome.BoostCreature);
this.staticText = "<br><i>Spell mastery</i> - If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it";
NecromanticSummoningReplacementEffect() {
super(Duration.EndOfStep, Outcome.BoostCreature);
}
public NecromanticSummoningEffect(final NecromanticSummoningEffect effect) {
NecromanticSummoningReplacementEffect(NecromanticSummoningReplacementEffect effect) {
super(effect);
}
@Override
public NecromanticSummoningEffect copy() {
return new NecromanticSummoningEffect(this);
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(getTargetPointer().getFirst(game, source))) {
return SpellMasteryCondition.instance.apply(game, source);
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(2), source, game);
return true;
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(2), source, game, event.getAppliedEffects());
discard();
}
return false;
}
@Override
public NecromanticSummoningReplacementEffect copy() {
return new NecromanticSummoningReplacementEffect(this);
}
}

View file

@ -58,7 +58,7 @@ import mage.target.targetpointer.FixedTarget;
public class OonasBlackguard extends CardImpl {
public OonasBlackguard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add("Faerie");
this.subtype.add("Rogue");
this.power = new MageInt(1);
@ -121,7 +121,7 @@ class OonasBlackguardReplacementEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source, game);
creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
return false;
}

View file

@ -57,7 +57,7 @@ import mage.target.common.TargetCreaturePermanent;
public class OtherworldlyJourney extends CardImpl {
public OtherworldlyJourney(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
this.subtype.add("Arcane");
// Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.
@ -184,7 +184,7 @@ class OtherworldlyJourneyEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
permanent.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
discard(); // use only once
}
return false;

View file

@ -56,7 +56,7 @@ import mage.target.common.TargetControlledCreaturePermanent;
public class SageOfFables extends CardImpl {
public SageOfFables(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.subtype.add("Merfolk");
this.subtype.add("Wizard");
this.power = new MageInt(2);
@ -115,7 +115,7 @@ class SageOfFablesReplacementEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source, game);
creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
return false;
}

View file

@ -64,7 +64,7 @@ import mage.watchers.Watcher;
public class SavageSummoning extends CardImpl {
public SavageSummoning(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}");
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}");
// Savage Summoning can't be countered.
Ability ability = new CantBeCounteredAbility();
@ -315,7 +315,7 @@ class SavageSummoningEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source, game);
creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
discard();
return false;

View file

@ -49,8 +49,8 @@ import mage.players.Player;
public class ThoughtReflection extends CardImpl {
public ThoughtReflection(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{U}{U}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{U}{U}");
// If you would draw a card, draw two cards instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThoughtReflectionReplacementEffect()));
@ -90,13 +90,13 @@ class ThoughtReflectionReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_CARD;
}
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(source.getControllerId());
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player you = game.getPlayer(event.getPlayerId());
@ -104,5 +104,5 @@ class ThoughtReflectionReplacementEffect extends ReplacementEffectImpl {
you.drawCards(2, game, event.getAppliedEffects());
}
return true;
}
}
}

View file

@ -30,17 +30,20 @@ package mage.cards.v;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.common.ManaWasSpentCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCardInYourGraveyard;
@ -51,12 +54,14 @@ import mage.target.common.TargetCardInYourGraveyard;
public class VigorMortis extends CardImpl {
public VigorMortis(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}");
// Return target creature card from your graveyard to the battlefield. If {G} was spent to cast Vigor Mortis, that creature enters the battlefield with an additional +1/+1 counter on it.
this.getSpellAbility().addEffect(new VigorMortisReplacementEffect()); // has to be added before the moving effect
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addEffect(new InfoEffect("If {G} was spent to cast {this}, that creature enters the battlefield with an additional +1/+1 counter on it"));
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard()));
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new VigorMortisAddCounterEffect(), new ManaWasSpentCondition(ColoredManaSymbol.G)));
}
public VigorMortis(final VigorMortis card) {
@ -69,29 +74,46 @@ public class VigorMortis extends CardImpl {
}
}
class VigorMortisAddCounterEffect extends OneShotEffect {
VigorMortisAddCounterEffect() {
super(Outcome.BoostCreature);
this.staticText = "that creature enters the battlefield with an additional +1/+1 counter on it";
class VigorMortisReplacementEffect extends ReplacementEffectImpl {
VigorMortisReplacementEffect() {
super(Duration.EndOfStep, Outcome.BoostCreature);
}
VigorMortisAddCounterEffect(final VigorMortisAddCounterEffect effect) {
VigorMortisReplacementEffect(VigorMortisReplacementEffect effect) {
super(effect);
}
@Override
public VigorMortisAddCounterEffect copy() {
return new VigorMortisAddCounterEffect(this);
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean apply(Game game, Ability source) {
// targetPointer can't be used because target moved from graveyard to battlefield
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(getTargetPointer().getFirst(game, source))) {
return new ManaWasSpentCondition(ColoredManaSymbol.G).apply(game, source);
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
discard();
}
return false;
}
@Override
public VigorMortisReplacementEffect copy() {
return new VigorMortisReplacementEffect(this);
}
}

View file

@ -55,7 +55,7 @@ import mage.target.common.TargetControlledCreaturePermanent;
public class ZameckGuildmage extends CardImpl {
public ZameckGuildmage(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}");
this.subtype.add("Elf");
this.subtype.add("Wizard");
@ -112,7 +112,7 @@ class ZameckGuildmageEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent target = ((EntersTheBattlefieldEvent) event).getTarget();
if (target != null) {
target.addCounters(CounterType.P1P1.createInstance(), source, game);
target.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects());
}
return false;
}