diff --git a/Mage.Sets/src/mage/sets/avacynrestored/SecondGuess.java b/Mage.Sets/src/mage/sets/avacynrestored/SecondGuess.java index 9d9621a6510..3b177bc92e7 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/SecondGuess.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/SecondGuess.java @@ -77,7 +77,7 @@ class SecondSpellPredicate implements Predicate { @Override public boolean apply(Spell input, Game game) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher.getSpellOrder(new MageObjectReference(input.getId(), game), game) == 2) { return true; diff --git a/Mage.Sets/src/mage/sets/darkascension/CurseOfExhaustion.java b/Mage.Sets/src/mage/sets/darkascension/CurseOfExhaustion.java index 84cb8b11fcb..200eefa2a31 100644 --- a/Mage.Sets/src/mage/sets/darkascension/CurseOfExhaustion.java +++ b/Mage.Sets/src/mage/sets/darkascension/CurseOfExhaustion.java @@ -104,7 +104,7 @@ class CurseOfExhaustionEffect extends ContinuousRuleModifyingEffectImpl { if (enchantment != null && enchantment.getAttachedTo() != null) { Player player = game.getPlayer(enchantment.getAttachedTo()); if (player != null && event.getPlayerId().equals(player.getId())) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) > 0) { return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/HardenedBerserker.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/HardenedBerserker.java index 383209aa32b..16a06295d51 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/HardenedBerserker.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/HardenedBerserker.java @@ -88,7 +88,7 @@ class HardenedBerserkerSpellsCostReductionEffect extends CostModificationEffectI @Override public void init(Ability source, Game game) { super.init(source, game); - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null) { spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()); } @@ -102,7 +102,7 @@ class HardenedBerserkerSpellsCostReductionEffect extends CostModificationEffectI @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null) { if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) { discard(); // only one use diff --git a/Mage.Sets/src/mage/sets/eventide/DreamThief.java b/Mage.Sets/src/mage/sets/eventide/DreamThief.java index a3dc86a47d3..8e2ec575f4a 100644 --- a/Mage.Sets/src/mage/sets/eventide/DreamThief.java +++ b/Mage.Sets/src/mage/sets/eventide/DreamThief.java @@ -29,7 +29,6 @@ package mage.sets.eventide; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; @@ -39,14 +38,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.WatcherScope; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; -import mage.watchers.Watcher; +import mage.watchers.common.SpellsCastWatcher; /** * @@ -69,7 +63,8 @@ public class DreamThief extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Thief enters the battlefield, draw a card if you've cast another blue spell this turn. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule)), new DreamThiefWatcher(this.getId())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule)), + new SpellsCastWatcher()); } @@ -87,55 +82,14 @@ class CastBlueSpellThisTurnCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - DreamThiefWatcher watcher = (DreamThiefWatcher) game.getState().getWatchers().get("DreamThiefWatcher", source.getControllerId()); + SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName()); if (watcher != null) { - return watcher.conditionMet(); + for (Spell spell : watcher.getSpellsCastThisTurn(source.getControllerId())) { + if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) { + return true; + } + } } return false; } } - -class DreamThiefWatcher extends Watcher { - - private static final FilterSpell filter = new FilterSpell(); - - static { - filter.add(new ColorPredicate(ObjectColor.BLUE)); - } - - private UUID cardId; - - public DreamThiefWatcher(UUID cardId) { - super("DreamThiefWatcher", WatcherScope.PLAYER); - this.cardId = cardId; - } - - public DreamThiefWatcher(final DreamThiefWatcher watcher) { - super(watcher); - this.cardId = watcher.cardId; - } - - @Override - public DreamThiefWatcher copy() { - return new DreamThiefWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured - return; - } - if (event.getType() == EventType.SPELL_CAST - && controllerId.equals(event.getPlayerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!spell.getSourceId().equals(cardId) && filter.match(spell, game)) { - condition = true; - } - } - } - - @Override - public void reset() { - super.reset(); - } -} diff --git a/Mage.Sets/src/mage/sets/futuresight/StormEntity.java b/Mage.Sets/src/mage/sets/futuresight/StormEntity.java index 820aee7e385..30d3c0a4c79 100644 --- a/Mage.Sets/src/mage/sets/futuresight/StormEntity.java +++ b/Mage.Sets/src/mage/sets/futuresight/StormEntity.java @@ -80,7 +80,7 @@ class OtherSpellsCastThisTurnCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); return watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() - 1; } diff --git a/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java b/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java index e6e49c66bd3..0a5a3b84e57 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java +++ b/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java @@ -95,7 +95,7 @@ class IncursionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(controllerId)) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) { return true; } diff --git a/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java b/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java index d71cf015bca..c32aa2403fb 100644 --- a/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java +++ b/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java @@ -141,7 +141,7 @@ class AngelicArbiterCantAttackTargetEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { if (game.getActivePlayerId().equals(permanent.getControllerId()) && game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(permanent.getControllerId()) > 0) { return true; } diff --git a/Mage.Sets/src/mage/sets/oathofthegatewatch/JoriEnRuinDiver.java b/Mage.Sets/src/mage/sets/oathofthegatewatch/JoriEnRuinDiver.java index 88396f9c5b1..7e0400433bd 100644 --- a/Mage.Sets/src/mage/sets/oathofthegatewatch/JoriEnRuinDiver.java +++ b/Mage.Sets/src/mage/sets/oathofthegatewatch/JoriEnRuinDiver.java @@ -92,7 +92,7 @@ class JoriEnTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(controllerId)) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) { return true; } diff --git a/Mage.Sets/src/mage/sets/oathofthegatewatch/PyromancersAssault.java b/Mage.Sets/src/mage/sets/oathofthegatewatch/PyromancersAssault.java index 20b0ac73ed9..0f6846ae2aa 100644 --- a/Mage.Sets/src/mage/sets/oathofthegatewatch/PyromancersAssault.java +++ b/Mage.Sets/src/mage/sets/oathofthegatewatch/PyromancersAssault.java @@ -90,7 +90,7 @@ class PyromancersAssaultTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(controllerId)) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) { return true; } diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ErayoSoratamiAscendant.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ErayoSoratamiAscendant.java index 1df3ededb7d..b98465fdfe4 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ErayoSoratamiAscendant.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ErayoSoratamiAscendant.java @@ -103,7 +103,7 @@ class ErayoSoratamiAscendantTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); return watcher != null && watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() == 4; } @@ -152,7 +152,7 @@ class ErayosEssenceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 1) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); diff --git a/Mage.Sets/src/mage/sets/seventhedition/Impatience.java b/Mage.Sets/src/mage/sets/seventhedition/Impatience.java index 1e3ae7e8495..ea5e75ccf15 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/Impatience.java +++ b/Mage.Sets/src/mage/sets/seventhedition/Impatience.java @@ -73,7 +73,7 @@ class ImpatienceCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); return watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(game.getActivePlayerId()) == 0; } diff --git a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java index 0dd3fc178fd..d0a626ad505 100644 --- a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java @@ -27,25 +27,20 @@ */ package mage.sets.worldwake; +import java.util.List; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.Watcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; /** * @@ -58,16 +53,13 @@ public class PermafrostTrap extends CardImpl { this.expansionSetCode = "WWK"; this.subtype.add("Trap"); - // If an opponent had a green creature enter the battlefield under his or her control this turn, you may pay {U} rather than pay Permafrost Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new PermafrostTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), PermafrostTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher()); // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. - TargetCreaturePermanent target = new TargetCreaturePermanent(0, 2); - this.getSpellAbility().addTarget(target); - this.getSpellAbility().addEffect(new PermafrostTrapEffect()); - - this.getSpellAbility().addWatcher(new PermafrostTrapWatcher()); + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); } public PermafrostTrap(final PermafrostTrap card) { @@ -80,99 +72,34 @@ public class PermafrostTrap extends CardImpl { } } -class PermafrostTrapWatcher extends Watcher { +class PermafrostTrapCondition implements Condition { - public PermafrostTrapWatcher() { - super("PermafrostTrapWatcher", WatcherScope.GAME); - } + private static final PermafrostTrapCondition fInstance = new PermafrostTrapCondition(); - public PermafrostTrapWatcher(final PermafrostTrapWatcher watcher) { - super(watcher); - } - - @Override - public PermafrostTrapWatcher copy() { - return new PermafrostTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) { // no need to check - condition has already occured - return; - } - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor(game).contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) { - condition = true; - } - } - } - - @Override - public void reset() { - super.reset(); - condition = false; - } -} - -class PermafrostTrapAlternativeCost extends AlternativeCostImpl { - - public PermafrostTrapAlternativeCost() { - super("you may pay {U} rather than pay Permafrost Trap's mana cost"); - this.add(new ColoredManaCost(ColoredManaSymbol.U)); - } - - public PermafrostTrapAlternativeCost(final PermafrostTrapAlternativeCost cost) { - super(cost); - } - - @Override - public PermafrostTrapAlternativeCost copy() { - return new PermafrostTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - PermafrostTrapWatcher watcher = (PermafrostTrapWatcher) game.getState().getWatchers().get("PermafrostTrapWatcher"); - if (watcher != null && watcher.conditionMet()) { - return true; - } - return false; - } - - @Override - public String getText() { - return "If an opponent had a green creature enter the battlefield under his or her control this turn, you may pay {U} rather than pay Permafrost Trap's mana cost"; - } -} - -class PermafrostTrapEffect extends OneShotEffect { - - public PermafrostTrapEffect() { - super(Outcome.Detriment); - staticText = "Tap up to two target creatures. Those creatures don't untap during their controller's next untap step"; - } - - public PermafrostTrapEffect(final PermafrostTrapEffect effect) { - super(effect); + public static Condition getInstance() { + return fInstance; } @Override public boolean apply(Game game, Ability source) { - for (UUID targetId : this.targetPointer.getTargets(game, source)) { - Permanent creature = game.getPermanent(targetId); - if (creature != null) { - creature.tap(game); - DontUntapInControllersNextUntapStepTargetEffect effect = new DontUntapInControllersNextUntapStepTargetEffect(); - effect.setTargetPointer(new FixedTarget(targetId)); - game.addEffect(effect, source); + PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List permanents = watcher.getThisTurnEnteringPermanents(opponentId); + if (permanents != null) { + for (Permanent permanent : permanents) { + if (permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor(game).isGreen()) { + return true; + } + } + } } } return false; } @Override - public PermafrostTrapEffect copy() { - return new PermafrostTrapEffect(this); + public String toString() { + return "If an opponent had a green creature enter the battlefield under his or her control this turn"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java index 0b00752ac28..eca69e7e9be 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java @@ -27,13 +27,12 @@ */ package mage.sets.worldwake; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.mana.ManaCost; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.PreventionEffectImpl; @@ -42,7 +41,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -51,7 +49,7 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSource; import mage.target.common.TargetCreatureOrPlayer; -import mage.watchers.Watcher; +import mage.watchers.common.SpellsCastWatcher; /** * @@ -65,13 +63,11 @@ public class RefractionTrap extends CardImpl { this.subtype.add("Trap"); // If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay Refraction Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new RefractionTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{W}"), RefractionTrapCondition.getInstance()), new SpellsCastWatcher()); // Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, Refraction Trap deals that much damage to target creature or player. this.getSpellAbility().addEffect(new RefractionTrapPreventDamageEffect(Duration.EndOfTurn, 3)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - - this.getSpellAbility().addWatcher(new RefractionTrapWatcher()); } public RefractionTrap(final RefractionTrap card) { @@ -84,89 +80,43 @@ public class RefractionTrap extends CardImpl { } } -class RefractionTrapWatcher extends Watcher { +class RefractionTrapCondition implements Condition { - Set playersMetCondition = new HashSet<>(); + private static final RefractionTrapCondition fInstance = new RefractionTrapCondition(); - public RefractionTrapWatcher() { - super("RefractionTrapWatcher", WatcherScope.GAME); - } - - public RefractionTrapWatcher(final RefractionTrapWatcher watcher) { - super(watcher); - this.playersMetCondition.addAll(watcher.playersMetCondition); + public static Condition getInstance() { + return fInstance; } @Override - public RefractionTrapWatcher copy() { - return new RefractionTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor(game).isRed()) { - if (spell.getCardType().contains(CardType.INSTANT) - || spell.getCardType().contains(CardType.SORCERY)) { - playersMetCondition.add(event.getPlayerId()); - } - } - } - } - - public boolean conditionMetForAnOpponent(UUID controllerId, Game game) { - Player controller = game.getPlayer(controllerId); - if (controller != null) { - for (UUID playerId : playersMetCondition) { - if (controller.hasOpponent(playerId, game)) { - return true; + public boolean apply(Game game, Ability source) { + SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List spells = watcher.getSpellsCastThisTurn(opponentId); + if (spells != null) { + for (Spell spell : spells) { + if ((spell.getCardType().contains(CardType.SORCERY) || spell.getCardType().contains(CardType.INSTANT)) + && spell.getColor(game).isRed()) { + return true; + } + } } } } return false; - } @Override - public void reset() { - playersMetCondition.clear(); - super.reset(); - } -} - -class RefractionTrapAlternativeCost extends AlternativeCostImpl { - - public RefractionTrapAlternativeCost() { - super("You may pay {W} rather than pay Refraction Trap's mana cost"); - this.add(new ManaCostsImpl("{W}")); - } - - public RefractionTrapAlternativeCost(final RefractionTrapAlternativeCost cost) { - super(cost); - } - - @Override - public RefractionTrapAlternativeCost copy() { - return new RefractionTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - RefractionTrapWatcher watcher = (RefractionTrapWatcher) game.getState().getWatchers().get("RefractionTrapWatcher"); - return watcher != null && watcher.conditionMetForAnOpponent(source.getControllerId(), game); - } - - @Override - public String getText() { - return "If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay {this} mana cost"; + public String toString() { + return "If an opponent cast a red instant or sorcery spell this turn"; } } class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { private final TargetSource target; - private int amount; + private final int amount; public RefractionTrapPreventDamageEffect(Duration duration, int amount) { super(duration, amount, false, false); diff --git a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java index f831a87dc80..850fe754c15 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java @@ -27,26 +27,22 @@ */ package mage.sets.worldwake; +import java.util.List; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; import mage.cards.CardImpl; -import mage.constants.ColoredManaSymbol; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.target.TargetSpell; -import mage.watchers.Watcher; +import mage.watchers.common.SpellsCastWatcher; /** * @@ -65,15 +61,14 @@ public class RicochetTrap extends CardImpl { this.expansionSetCode = "WWK"; this.subtype.add("Trap"); - // If an opponent cast a blue spell this turn, you may pay {R} rather than pay Ricochet Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new RicochetTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{R}"), RicochetTrapCondition.getInstance())); // Change the target of target spell with a single target. this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect(true, true)); this.getSpellAbility().addTarget(new TargetSpell(filter)); - this.getSpellAbility().addWatcher(new RicochetTrapWatcher()); + this.getSpellAbility().addWatcher(new SpellsCastWatcher()); } public RicochetTrap(final RicochetTrap card) { @@ -86,70 +81,34 @@ public class RicochetTrap extends CardImpl { } } -class RicochetTrapWatcher extends Watcher { +class RicochetTrapCondition implements Condition { - public RicochetTrapWatcher() { - super("RicochetTrapWatcher", WatcherScope.GAME); - } + private static final RicochetTrapCondition fInstance = new RicochetTrapCondition(); - public RicochetTrapWatcher(final RicochetTrapWatcher watcher) { - super(watcher); + public static Condition getInstance() { + return fInstance; } @Override - public RicochetTrapWatcher copy() { - return new RicochetTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) //no need to check - condition has already occured - { - return; - } - if (event.getType() == EventType.SPELL_CAST - && game.getOpponents(controllerId).contains(event.getPlayerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor(game).isBlue()) { - condition = true; + public boolean apply(Game game, Ability source) { + SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List spells = watcher.getSpellsCastThisTurn(opponentId); + if (spells != null) { + for (Spell spell : spells) { + if (spell.getColor(game).isBlue()) { + return true; + } + } + } } } - } - - @Override - public void reset() { - super.reset(); - condition = false; - } -} - -class RicochetTrapAlternativeCost extends AlternativeCostImpl { - - public RicochetTrapAlternativeCost() { - super("You may pay {R} rather than pay Ricochet Trap's mana cost"); - this.add(new ColoredManaCost(ColoredManaSymbol.R)); - } - - public RicochetTrapAlternativeCost(final RicochetTrapAlternativeCost cost) { - super(cost); - } - - @Override - public RicochetTrapAlternativeCost copy() { - return new RicochetTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - RicochetTrapWatcher watcher = (RicochetTrapWatcher) game.getState().getWatchers().get("RicochetTrapWatcher"); - if (watcher != null && watcher.conditionMet()) { - return true; - } return false; } @Override - public String getText() { - return "If an opponent cast a blue spell this turn, you may pay {R} rather than pay {this} mana cost"; + public String toString() { + return "If an opponent cast a blue spell this turn"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java index 6d3fa2bceeb..f129921337d 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java @@ -27,17 +27,16 @@ */ package mage.sets.worldwake; -import java.util.List; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.Cost; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; @@ -61,9 +60,8 @@ public class SlingbowTrap extends CardImpl { this.expansionSetCode = "WWK"; this.subtype.add("Trap"); - // If a black creature with flying is attacking, you may pay {G} rather than pay Slingbow Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new SlingbowTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{G}"), SlingbowTrapCondition.getInstance())); // Destroy target attacking creature with flying. this.getSpellAbility().addEffect(new DestroyTargetEffect()); @@ -80,37 +78,29 @@ public class SlingbowTrap extends CardImpl { } } -class SlingbowTrapAlternativeCost extends AlternativeCostImpl { +class SlingbowTrapCondition implements Condition { - public SlingbowTrapAlternativeCost() { - super("you may pay {G} rather than pay {this}'s mana cost"); - this.add(new ManaCostsImpl("{G}")); - } + private static final SlingbowTrapCondition fInstance = new SlingbowTrapCondition(); - public SlingbowTrapAlternativeCost(final SlingbowTrapAlternativeCost cost) { - super(cost); + public static Condition getInstance() { + return fInstance; } @Override - public SlingbowTrapAlternativeCost copy() { - return new SlingbowTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - List attackers = game.getCombat().getAttackers(); - for (UUID creatureId : attackers) { - Permanent creature = game.getPermanent(creatureId); - if (creature.getColor(game).isBlack() - && creature.getAbilities().contains(FlyingAbility.getInstance())) { - return true; + public boolean apply(Game game, Ability source) { + for (UUID attackingCreatureId : game.getCombat().getAttackers()) { + Permanent attackingCreature = game.getPermanent(attackingCreatureId); + if (attackingCreature != null) { + if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance().getId(), game)) { + return true; + } } } return false; } @Override - public String getText() { - return "If a black creature with flying is attacking, you may pay {G} rather than pay Slingbow Trap's mana cost"; + public String toString() { + return "If a black creature with flying is attacking"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java b/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java index a83b34aa210..eafbf913ac2 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java @@ -29,7 +29,8 @@ package mage.sets.zendikar; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; @@ -50,7 +51,7 @@ public class ArrowVolleyTrap extends CardImpl { this.subtype.add("Trap"); // If four or more creatures are attacking, you may pay {1}{W} rather than pay Arrow Volley Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new ArrowVolleyTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{1}{W}"), ArrowVolleyTrapCondition.getInstance())); // Arrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); @@ -68,29 +69,21 @@ public class ArrowVolleyTrap extends CardImpl { } } -class ArrowVolleyTrapAlternativeCost extends AlternativeCostImpl { +class ArrowVolleyTrapCondition implements Condition { - public ArrowVolleyTrapAlternativeCost() { - super("you may pay {1}{W} rather than pay Arrow Volley Trap's mana cost"); - this.add(new ManaCostsImpl("{1}{W}")); - } + private static final ArrowVolleyTrapCondition fInstance = new ArrowVolleyTrapCondition(); - public ArrowVolleyTrapAlternativeCost(final ArrowVolleyTrapAlternativeCost cost) { - super(cost); + public static Condition getInstance() { + return fInstance; } @Override - public ArrowVolleyTrapAlternativeCost copy() { - return new ArrowVolleyTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { + public boolean apply(Game game, Ability source) { return game.getCombat().getAttackers().size() > 3; } @Override - public String getText() { - return "If four or more creatures are attacking, you may pay {1}{W} rather than pay {this}'s mana cost"; + public String toString() { + return "If four or more creatures are attacking"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java b/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java index 492af7f4d46..47fff01195c 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java @@ -27,20 +27,20 @@ */ package mage.sets.zendikar; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.BeastToken2; -import mage.watchers.Watcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; /** * @@ -54,8 +54,7 @@ public class BalothCageTrap extends CardImpl { this.subtype.add("Trap"); // If an opponent had an artifact enter the battlefield under his or her control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new BalothCageTrapAlternativeCost()); - this.getSpellAbility().addWatcher(new BalothCageTrapWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{1}{G}"), BalothCageTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher()); // Put a 4/4 green Beast creature token onto the battlefield. this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken2())); @@ -71,68 +70,34 @@ public class BalothCageTrap extends CardImpl { } } -class BalothCageTrapWatcher extends Watcher { +class BalothCageTrapCondition implements Condition { - public BalothCageTrapWatcher() { - super("BalothCageTrapWatcher", WatcherScope.GAME); - } + private static final BalothCageTrapCondition fInstance = new BalothCageTrapCondition(); - public BalothCageTrapWatcher(final BalothCageTrapWatcher watcher) { - super(watcher); + public static Condition getInstance() { + return fInstance; } @Override - public BalothCageTrapWatcher copy() { - return new BalothCageTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) { // no need to check - condition has already occured - return; - } - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.ARTIFACT) && !perm.getControllerId().equals(controllerId)) { - condition = true; + public boolean apply(Game game, Ability source) { + PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List permanents = watcher.getThisTurnEnteringPermanents(opponentId); + if (permanents != null) { + for (Permanent permanent : permanents) { + if (permanent.getCardType().contains(CardType.ARTIFACT)) { + return true; + } + } + } } } - } - - @Override - public void reset() { - super.reset(); - condition = false; - } -} - -class BalothCageTrapAlternativeCost extends AlternativeCostImpl { - - public BalothCageTrapAlternativeCost() { - super("you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost"); - this.add(new ManaCostsImpl("{1}{G}")); - } - - public BalothCageTrapAlternativeCost(final BalothCageTrapAlternativeCost cost) { - super(cost); - } - - @Override - public BalothCageTrapAlternativeCost copy() { - return new BalothCageTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - BalothCageTrapWatcher watcher = (BalothCageTrapWatcher) game.getState().getWatchers().get("BalothCageTrapWatcher"); - if (watcher != null && watcher.conditionMet()) { - return true; - } return false; } @Override - public String getText() { - return "If an opponent had an artifact enter the battlefield under his or her control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost"; + public String toString() { + return "If an opponent had an artifact enter the battlefield under his or her control this turn"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java index e9e07a06bb3..b38f8e7b0fd 100644 --- a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java @@ -28,15 +28,14 @@ package mage.sets.zendikar; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.costs.AlternativeCostImpl; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; @@ -54,11 +53,10 @@ public class InfernoTrap extends CardImpl { this.expansionSetCode = "ZEN"; this.subtype.add("Trap"); - // If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay Inferno Trap's mana cost. this.getSpellAbility().addAlternativeCost(new InfernoTrapAlternativeCost()); this.getSpellAbility().addWatcher(new ControllerDamagedByCreatureWatcher()); - + // Inferno Trap deals 4 damage to target creature. this.getSpellAbility().addEffect(new DamageTargetEffect(4)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java b/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java index 058b40ffe23..a51466ab87a 100644 --- a/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java @@ -27,26 +27,23 @@ */ package mage.sets.zendikar; -import java.util.HashMap; -import java.util.Map; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.Cost; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandPermanent; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetLandPermanent; -import mage.watchers.Watcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; /** * @@ -60,8 +57,7 @@ public class LavaballTrap extends CardImpl { this.subtype.add("Trap"); // If an opponent had two or more lands enter the battlefield under his or her control this turn, you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new LavaballTrapAlternativeCost()); - this.getSpellAbility().addWatcher(new LavaballTrapWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{3}{R}{R}"), LavaballTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher()); // Destroy two target lands. Lavaball Trap deals 4 damage to each creature. this.getSpellAbility().addEffect(new DestroyTargetEffect()); @@ -80,87 +76,38 @@ public class LavaballTrap extends CardImpl { } } -class LavaballTrapWatcher extends Watcher { +class LavaballTrapCondition implements Condition { - private Map amountOfLandsPlayedThisTurn = new HashMap<>(); + private static final LavaballTrapCondition fInstance = new LavaballTrapCondition(); - public LavaballTrapWatcher() { - super("LavaballTrapWatcher", WatcherScope.GAME); - } - - public LavaballTrapWatcher(final LavaballTrapWatcher watcher) { - super(watcher); - for (Map.Entry entry : watcher.amountOfLandsPlayedThisTurn.entrySet()) { - amountOfLandsPlayedThisTurn.put(entry.getKey(), entry.getValue()); - } + public static Condition getInstance() { + return fInstance; } @Override - public LavaballTrapWatcher copy() { - return new LavaballTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.LAND)) { - Integer amount = amountOfLandsPlayedThisTurn.get(perm.getControllerId()); - if (amount == null) { - amount = 1; - } else { - ++amount; + public boolean apply(Game game, Ability source) { + PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List permanents = watcher.getThisTurnEnteringPermanents(opponentId); + if (permanents != null) { + int count = 0; + for (Permanent permanent : permanents) { + if (permanent.getCardType().contains(CardType.LAND)) { + count++; + if (count == 2) { + return true; + } + } + } } - amountOfLandsPlayedThisTurn.put(perm.getControllerId(), amount); } } - } - - public int maxLandsAnOpponentPlayedThisTurn(UUID playerId, Game game) { - int maxLands = 0; - for (UUID opponentId : game.getOpponents(playerId)) { - Integer amount = amountOfLandsPlayedThisTurn.get(opponentId); - if (amount != null && amount > maxLands) { - maxLands = amount; - } - } - return maxLands; - } - - @Override - public void reset() { - super.reset(); - amountOfLandsPlayedThisTurn.clear(); - } -} - -class LavaballTrapAlternativeCost extends AlternativeCostImpl { - - public LavaballTrapAlternativeCost() { - super("you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost"); - this.add(new ManaCostsImpl("{3}{R}{R}")); - } - - public LavaballTrapAlternativeCost(final LavaballTrapAlternativeCost cost) { - super(cost); - } - - @Override - public LavaballTrapAlternativeCost copy() { - return new LavaballTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - LavaballTrapWatcher watcher = (LavaballTrapWatcher) game.getState().getWatchers().get("LavaballTrapWatcher"); - if (watcher != null && watcher.maxLandsAnOpponentPlayedThisTurn(source.getControllerId(), game) > 1) { - return true; - } return false; } @Override - public String getText() { - return "If an opponent had two or more lands enter the battlefield under his or her control this turn, you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost"; + public String toString() { + return "If an opponent had two or more lands enter the battlefield under his or her control this turn"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/LethargyTrap.java b/Mage.Sets/src/mage/sets/zendikar/LethargyTrap.java index a52dc64ab3d..8dce3da8943 100644 --- a/Mage.Sets/src/mage/sets/zendikar/LethargyTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/LethargyTrap.java @@ -28,16 +28,15 @@ package mage.sets.zendikar; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.Cost; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; @@ -47,25 +46,24 @@ import mage.game.Game; * @author jeffwadsworth */ public class LethargyTrap extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures"); static { filter.add(new AttackingPredicate()); } - + public LethargyTrap(UUID ownerId) { super(ownerId, 51, "Lethargy Trap", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{U}"); this.expansionSetCode = "ZEN"; this.subtype.add("Trap"); - // If three or more creatures are attacking, you may pay {U} rather than pay Lethargy Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new LethargyTrapAlternativeCost()); - + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), LethargyTrapCondition.getInstance())); + // Attacking creatures get -3/-0 until end of turn. this.getSpellAbility().addEffect(new BoostAllEffect(-3, 0, Duration.EndOfTurn, filter, false)); - + } public LethargyTrap(final LethargyTrap card) { @@ -78,32 +76,21 @@ public class LethargyTrap extends CardImpl { } } -class LethargyTrapAlternativeCost extends AlternativeCostImpl { +class LethargyTrapCondition implements Condition { - public LethargyTrapAlternativeCost() { - super("you may pay {U} rather than pay Lethargy Trap's mana cost"); - this.add(new ManaCostsImpl("{U}")); - } + private static final LethargyTrapCondition fInstance = new LethargyTrapCondition(); - public LethargyTrapAlternativeCost(final LethargyTrapAlternativeCost cost) { - super(cost); + public static Condition getInstance() { + return fInstance; } @Override - public LethargyTrapAlternativeCost copy() { - return new LethargyTrapAlternativeCost(this); + public boolean apply(Game game, Ability source) { + return game.getCombat().getAttackers().size() > 2; } @Override - public boolean isAvailable(Game game, Ability source) { - if (game.getCombat().getAttackers().size() >= 3) { - return true; - } - return false; + public String toString() { + return "If three or more creatures are attacking"; } - - @Override - public String getText() { - return "If three or more creatures are attacking, you may pay {U} rather than pay Lethargy Trap's mana cost"; - } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java b/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java index 2378fd36984..258a92dbb81 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java @@ -27,28 +27,19 @@ */ package mage.sets.zendikar; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.WatcherScope; -import mage.constants.Zone; import mage.filter.FilterSpell; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; -import mage.players.Player; import mage.target.TargetSpell; -import mage.watchers.Watcher; +import mage.watchers.common.CastSpellLastTurnWatcher; /** * @@ -63,14 +54,12 @@ public class MindbreakTrap extends CardImpl { this.expansionSetCode = "ZEN"; this.subtype.add("Trap"); - // If an opponent cast three or more spells this turn, you may pay {0} rather than pay Mindbreak Trap's mana cost. - this.getSpellAbility().addAlternativeCost( - new MindbreakTrapAlternativeCost()); - this.getSpellAbility().addWatcher(new MindbreakTrapWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new GenericManaCost(0), MindbreakTrapCondition.getInstance())); + // Exile any number of target spells. this.getSpellAbility().addTarget(new TargetSpell(0, Integer.MAX_VALUE, filter)); - this.getSpellAbility().addEffect(new MindbreakEffect()); + this.getSpellAbility().addEffect(new ExileTargetEffect("Exile any number of target spells")); } public MindbreakTrap(final MindbreakTrap card) { @@ -83,116 +72,30 @@ public class MindbreakTrap extends CardImpl { } } -class MindbreakTrapWatcher extends Watcher { +class MindbreakTrapCondition implements Condition { - private Map counts = new HashMap<>(); + private static final MindbreakTrapCondition fInstance = new MindbreakTrapCondition(); - public MindbreakTrapWatcher() { - super("opponent cast three or more spells", WatcherScope.PLAYER); - } - - public MindbreakTrapWatcher(final MindbreakTrapWatcher watcher) { - super(watcher); - for (Entry entry: watcher.counts.entrySet()) { - counts.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public MindbreakTrapWatcher copy() { - return new MindbreakTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) { // no need to check - condition has already occured - return; - } - if (event.getType() == EventType.SPELL_CAST - && game.getOpponents(controllerId).contains(event.getPlayerId())) { - int count = 1; - if (counts.containsKey(event.getPlayerId())) { - count += counts.get(event.getPlayerId()); - if (count >= 3) { - condition = true; - } - } - counts.put(event.getPlayerId(), count); - } - } - - @Override - public void reset() { - super.reset(); - counts.clear(); - } - -} - -class MindbreakTrapAlternativeCost extends AlternativeCostImpl { - - public MindbreakTrapAlternativeCost() { - super("you may pay {0} rather than pay {this}'s mana cost"); - this.add(new GenericManaCost(0)); - } - - public MindbreakTrapAlternativeCost(final MindbreakTrapAlternativeCost cost) { - super(cost); - } - - @Override - public MindbreakTrapAlternativeCost copy() { - return new MindbreakTrapAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("opponent cast three or more spells", source.getControllerId()); - if (watcher != null && watcher.conditionMet()) { - return true; - } - return false; - } - - @Override - public String getText() { - return "If an opponent cast three or more spells this turn, you may pay {0} rather than pay {this}'s mana cost"; - } -} - -class MindbreakEffect extends OneShotEffect{ - - MindbreakEffect(MindbreakEffect effect) { - super(effect); - } - - MindbreakEffect() { - super(Outcome.Exile); - staticText = "Exile any number of target spells"; + public static Condition getInstance() { + return fInstance; } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int affectedTargets = 0; - if (targetPointer.getTargets(game, source).size() > 0) { - for (UUID spellId : targetPointer.getTargets(game, source)) { - Spell spell = game.getStack().getSpell(spellId); - if (spell != null) { - controller.moveCardToExileWithInfo(spell, null, "", source.getSourceId(), game, Zone.STACK, true); - affectedTargets++; - } + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(opponentId) > 2) { + return true; } } - return affectedTargets > 0; } return false; } @Override - public MindbreakEffect copy() { - return new MindbreakEffect(this); + public String toString() { + return "If an opponent cast three or more spells this turn"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/PitfallTrap.java b/Mage.Sets/src/mage/sets/zendikar/PitfallTrap.java index 7ba0ab5b4d7..7e446a6796d 100644 --- a/Mage.Sets/src/mage/sets/zendikar/PitfallTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/PitfallTrap.java @@ -29,7 +29,8 @@ package mage.sets.zendikar; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -60,7 +61,7 @@ public class PitfallTrap extends CardImpl { this.subtype.add("Trap"); // If exactly one creature is attacking, you may pay {W} rather than pay Pitfall Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new PitfallTrapAlternativeCost()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{W}"), PitfallTrapCondition.getInstance())); // Destroy target attacking creature without flying. this.getSpellAbility().addEffect(new DestroyTargetEffect()); @@ -77,32 +78,21 @@ public class PitfallTrap extends CardImpl { } } -class PitfallTrapAlternativeCost extends AlternativeCostImpl { +class PitfallTrapCondition implements Condition { - public PitfallTrapAlternativeCost() { - super("you may pay {W} rather than pay Pitfall Trap's mana cost"); - this.add(new ManaCostsImpl("{W}")); - } + private static final PitfallTrapCondition fInstance = new PitfallTrapCondition(); - public PitfallTrapAlternativeCost(final PitfallTrapAlternativeCost cost) { - super(cost); + public static Condition getInstance() { + return fInstance; } @Override - public PitfallTrapAlternativeCost copy() { - return new PitfallTrapAlternativeCost(this); + public boolean apply(Game game, Ability source) { + return game.getCombat().getAttackers().size() == 1; } @Override - public boolean isAvailable(Game game, Ability source) { - if (game.getCombat().getAttackers().size() == 1) { - return true; - } - return false; - } - - @Override - public String getText() { - return "If exactly one creature is attacking, you may pay {W} rather than pay Pitfall Trap's mana cost"; + public String toString() { + return "If exactly one creature is attacking"; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java b/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java index a9fcc4e1954..57c869c4bab 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java @@ -63,15 +63,10 @@ public class SummoningTrap extends CardImpl { this.expansionSetCode = "ZEN"; this.subtype.add("Trap"); - // If a creature spell you cast this turn was countered by a spell or - // ability an opponent controlled, you may pay {0} rather than pay - // Summoning Trap's mana cost. - this.getSpellAbility().addAlternativeCost( - new SummoningTrapAlternativeCost()); + // If a creature spell you cast this turn was countered by a spell or ability an opponent controlled, you may pay {0} rather than pay Summoning Trap's mana cost. + this.getSpellAbility().addAlternativeCost(new SummoningTrapAlternativeCost()); this.getSpellAbility().addWatcher(new SummoningTrapWatcher()); - // Look at the top seven cards of your library. You may put a creature - // card from among them onto the battlefield. Put the rest on the bottom - // of your library in any order. + // Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in any order. this.getSpellAbility().addEffect(new SummoningTrapEffect()); } diff --git a/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java b/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java index fc84669fdf3..8778efd3f3e 100644 --- a/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java @@ -27,23 +27,20 @@ */ package mage.sets.zendikar; -import java.util.HashMap; -import java.util.Map; +import java.util.List; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.AlternativeCostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; -import mage.constants.WatcherScope; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; /** * @@ -56,15 +53,13 @@ public class WhiplashTrap extends CardImpl { this.expansionSetCode = "ZEN"; this.subtype.add("Trap"); - // If an opponent had two or more creatures enter the battlefield under his or her control this turn, you may pay {U} rather than pay Whiplash Trap's mana cost. - this.getSpellAbility().addAlternativeCost(new WhiplashAlternativeCost()); - this.getSpellAbility().addWatcher(new WhiplashTrapWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), WhiplashTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher()); // Return two target creatures to their owners' hands. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); - + } public WhiplashTrap(final WhiplashTrap card) { @@ -77,87 +72,38 @@ public class WhiplashTrap extends CardImpl { } } -class WhiplashTrapWatcher extends Watcher { +class WhiplashTrapCondition implements Condition { - private Map amountOfCreaturesPlayedThisTurn = new HashMap<>(); + private static final WhiplashTrapCondition fInstance = new WhiplashTrapCondition(); - public WhiplashTrapWatcher() { - super("WhiplashTrapWatcher", WatcherScope.GAME); - } - - public WhiplashTrapWatcher(final WhiplashTrapWatcher watcher) { - super(watcher); - for (Map.Entry entry : watcher.amountOfCreaturesPlayedThisTurn.entrySet()) { - amountOfCreaturesPlayedThisTurn.put(entry.getKey(), entry.getValue()); - } + public static Condition getInstance() { + return fInstance; } @Override - public WhiplashTrapWatcher copy() { - return new WhiplashTrapWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.CREATURE)) { - Integer amount = amountOfCreaturesPlayedThisTurn.get(perm.getControllerId()); - if (amount == null) { - amount = 1; - } else { - ++amount; + public boolean apply(Game game, Ability source) { + PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.BASIC_KEY); + if (watcher != null) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + List permanents = watcher.getThisTurnEnteringPermanents(opponentId); + if (permanents != null) { + int count = 0; + for (Permanent permanent : permanents) { + if (permanent.getCardType().contains(CardType.CREATURE)) { + count++; + if (count == 2) { + return true; + } + } + } } - amountOfCreaturesPlayedThisTurn.put(perm.getControllerId(), amount); } } - } - - public int maxCreaturesAnOpponentPlayedThisTurn(UUID playerId, Game game) { - int maxCreatures = 0; - for (UUID opponentId : game.getOpponents(playerId)) { - Integer amount = amountOfCreaturesPlayedThisTurn.get(opponentId); - if (amount != null && amount > maxCreatures) { - maxCreatures = amount; - } - } - return maxCreatures; - } - - @Override - public void reset() { - super.reset(); - amountOfCreaturesPlayedThisTurn.clear(); - } -} - -class WhiplashAlternativeCost extends AlternativeCostImpl { - - public WhiplashAlternativeCost() { - super("you may pay {U} rather than pay Whiplash Trap's mana cost"); - this.add(new ManaCostsImpl("{U}")); - } - - public WhiplashAlternativeCost(final WhiplashAlternativeCost cost) { - super(cost); - } - - @Override - public WhiplashAlternativeCost copy() { - return new WhiplashAlternativeCost(this); - } - - @Override - public boolean isAvailable(Game game, Ability source) { - WhiplashTrapWatcher watcher = (WhiplashTrapWatcher) game.getState().getWatchers().get("WhiplashTrapWatcher"); - if (watcher != null && watcher.maxCreaturesAnOpponentPlayedThisTurn(source.getControllerId(), game) >= 2) { - return true; - } return false; } @Override - public String getText() { - return "If an opponent had two or more creatures enter the battlefield under his or her control this turn, you may pay {U} rather than pay {this}'s mana cost"; + public String toString() { + return "If an opponent had two or more creatures enter the battlefield under his or her control this turn"; } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/DefenseGridTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/DefenseGridTest.java new file mode 100644 index 00000000000..493aba19e6e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/DefenseGridTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DefenseGridTest extends CardTestPlayerBase { + + /** + * Defense Grid vs Mindbreak Trap Not sure how this is coded, but Mindbreak + * Trap should still cost 3 more (0+3=3). + * + */ + @Test + public void testCostIncrease() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + + // Each spell costs {3} more to cast except during its controller's turn. + addCard(Zone.BATTLEFIELD, playerA, "Defense Grid"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // If an opponent cast three or more spells this turn, you may pay {0} rather than pay Mindbreak Trap's mana cost. + // Exile any number of target spells. + addCard(Zone.HAND, playerB, "Mindbreak Trap"); // {2}{U}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mindbreak Trap", "Lightning Bolt^Lightning Bolt^Lightning Bolt"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount("Lightning Bolt", 3); + assertGraveyardCount(playerB, "Mindbreak Trap", 1); + + assertTappedCount("Island", true, 3); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 588dd63444a..6ab3d11fbc5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -300,7 +300,7 @@ public class TestPlayer implements Player { if (selectedMode == null) { throw new UnsupportedOperationException("Mode not available for " + ability.toString()); } - if (selectedMode.getTargets().size() == 0) { + if (selectedMode.getTargets().isEmpty()) { throw new AssertionError("Ability has no targets. " + ability.toString()); } if (index >= selectedMode.getTargets().size()) { @@ -319,27 +319,29 @@ public class TestPlayer implements Player { } } else { for (UUID id : currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { - MageObject object = game.getObject(id); - if (object != null - && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { - if (currentTarget.getNumberOfTargets() == 1) { - currentTarget.clearChosen(); + if (!currentTarget.getTargets().contains(id)) { + MageObject object = game.getObject(id); + if (object != null + && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { + if (currentTarget.getNumberOfTargets() == 1) { + currentTarget.clearChosen(); + } + if (currentTarget instanceof TargetCreaturePermanentAmount) { + // supports only to set the complete amount to one target + TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; + targetAmount.setAmount(ability, game); + int amount = targetAmount.getAmountRemaining(); + targetAmount.addTarget(id, amount, ability, game); + targetsSet++; + } else { + currentTarget.addTarget(id, ability, game); + targetsSet++; + } + if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { + index++; + } + break; } - if (currentTarget instanceof TargetCreaturePermanentAmount) { - // supports only to set the complete amount to one target - TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; - targetAmount.setAmount(ability, game); - int amount = targetAmount.getAmountRemaining(); - targetAmount.addTarget(id, amount, ability, game); - targetsSet++; - } else { - currentTarget.addTarget(id, ability, game); - targetsSet++; - } - if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { - index++; - } - break; } } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/NoSpellsWereCastLastTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/NoSpellsWereCastLastTurnCondition.java index 61d78f90ed8..254921948a7 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/NoSpellsWereCastLastTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/NoSpellsWereCastLastTurnCondition.java @@ -45,7 +45,7 @@ public class NoSpellsWereCastLastTurnCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); // if any player cast spell, return false for (Integer count : watcher.getAmountOfSpellsCastOnPrevTurn().values()) { if (count > 0) { diff --git a/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java index c716ebf8a3e..7d617df0c7a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java @@ -45,7 +45,7 @@ public class TwoOrMoreSpellsWereCastLastTurnCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); // if any player cast more than two spells, return true for (Integer count : watcher.getAmountOfSpellsCastOnPrevTurn().values()) { if (count >= 2) { diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index be4893b1b45..0b622c477bc 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -120,7 +120,8 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } private AlternativeCost2 convertToAlternativeCost(Cost cost) { - return cost != null ? new AlternativeCost2Impl(null, cost.getText(), cost) : null; + //return cost != null ? new AlternativeCost2Impl(null, cost.getText(), cost) : null; + return cost != null ? new AlternativeCost2Impl(null, "", cost) : null; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java index c08f9af669d..cc0eb23cf5d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java @@ -38,6 +38,8 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.targetpointer.FirstTargetPointer; @@ -133,6 +135,11 @@ public class ExileTargetEffect extends OneShotEffect { if (!currentZone.equals(Zone.EXILED) && (onlyFromZone == null || onlyFromZone.equals(currentZone))) { toExile.add(card); } + } else { + StackObject stackObject = game.getStack().getStackObject(targetId); + if (stackObject instanceof Spell && ((Spell) stackObject).getCard() != null) { + toExile.add(((Spell) stackObject).getCard()); + } } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java index 6f13383f28f..ee6ca009fbd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.continuous; import mage.abilities.Ability; @@ -43,14 +42,13 @@ import mage.watchers.common.CastSpellLastTurnWatcher; * * @author LevelX2 */ - public class CantCastMoreThanOneSpellEffect extends ContinuousRuleModifyingEffectImpl { private final TargetController targetController; - + public CantCastMoreThanOneSpellEffect(TargetController targetController) { super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.targetController = targetController; + this.targetController = targetController; } public CantCastMoreThanOneSpellEffect(final CantCastMoreThanOneSpellEffect effect) { @@ -95,22 +93,22 @@ public class CantCastMoreThanOneSpellEffect extends ContinuousRuleModifyingEffec Permanent attachment = game.getPermanent(source.getSourceId()); if (attachment == null || !attachment.getAttachedTo().equals(event.getPlayerId())) { return false; - } + } } - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); - if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId())> 0) { + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); + if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) > 0) { return true; } return false; } - + @Override public String getText(Mode mode) { if (staticText != null && !staticText.isEmpty()) { return staticText; } StringBuilder sb = new StringBuilder(); - switch(targetController) { + switch (targetController) { case YOU: sb.append("You"); break; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java index 072e2360ef3..e0fc0786f41 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.ruleModifying; import mage.abilities.Ability; @@ -40,15 +39,15 @@ import mage.watchers.common.CastSpellLastTurnWatcher; * * @author LoneFox */ - public class CastOnlyIfYouHaveCastAnotherSpellEffect extends ContinuousRuleModifyingEffectImpl { + public CastOnlyIfYouHaveCastAnotherSpellEffect() { - super(Duration.EndOfGame, Outcome.Detriment); - staticText = "Cast {this} only if you've cast another spell this turn"; + super(Duration.EndOfGame, Outcome.Detriment); + staticText = "Cast {this} only if you've cast another spell this turn"; } public CastOnlyIfYouHaveCastAnotherSpellEffect(final CastOnlyIfYouHaveCastAnotherSpellEffect effect) { - super(effect); + super(effect); } @Override @@ -58,22 +57,22 @@ public class CastOnlyIfYouHaveCastAnotherSpellEffect extends ContinuousRuleModif @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getSourceId().equals(source.getSourceId())) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); - if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) == 0) { - return true; - } - } - return false; + if (event.getSourceId().equals(source.getSourceId())) { + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); + if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) == 0) { + return true; + } + } + return false; } @Override public boolean apply(Game game, Ability source) { - return true; + return true; } @Override public CastOnlyIfYouHaveCastAnotherSpellEffect copy() { - return new CastOnlyIfYouHaveCastAnotherSpellEffect(this); + return new CastOnlyIfYouHaveCastAnotherSpellEffect(this); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java index 5c54c47d401..27ff6fba579 100644 --- a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java @@ -100,7 +100,7 @@ class StormEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { MageObjectReference spellRef = (MageObjectReference) this.getValue("StormSpellRef"); if (spellRef != null) { - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); int stormCount = watcher.getSpellOrder(spellRef, game) - 1; if (stormCount > 0) { Spell spell = (Spell) this.getValue("StormSpell"); diff --git a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java index 6b82e3087af..e63a2f44fb5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java @@ -69,7 +69,7 @@ public class SurgeAbility extends SpellAbility { @Override public boolean canActivate(UUID playerId, Game game) { // check if controller or teammate has already cast a spell this turn - CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher"); + CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName()); if (watcher != null) { Player player = game.getPlayer(playerId); if (player != null) { diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java index e9d8520293c..f00e3354a00 100644 --- a/Mage/src/main/java/mage/watchers/Watcher.java +++ b/Mage/src/main/java/mage/watchers/Watcher.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.watchers; import java.io.Serializable; @@ -42,20 +41,19 @@ import mage.game.events.GameEvent; */ public abstract class Watcher implements Serializable { + protected String basicKey; protected UUID controllerId; protected UUID sourceId; - protected String key; protected boolean condition; protected WatcherScope scope; - public Watcher(String key, WatcherScope scope) { - this.key = key; + public Watcher(String basicKey, WatcherScope scope) { + this.basicKey = basicKey; this.scope = scope; } public Watcher(final Watcher watcher) { this.condition = watcher.condition; - this.key = watcher.key; this.controllerId = watcher.controllerId; this.sourceId = watcher.sourceId; this.scope = watcher.scope; @@ -80,19 +78,19 @@ public abstract class Watcher implements Serializable { public String getKey() { switch (scope) { case GAME: - return key; + return basicKey; case PLAYER: - return controllerId + key; + return controllerId + basicKey; case CARD: - return sourceId + key; + return sourceId + basicKey; } - return key; + return basicKey; } public boolean conditionMet() { return condition; } - + public void reset() { condition = false; } @@ -100,4 +98,5 @@ public abstract class Watcher implements Serializable { public abstract void watch(GameEvent event, Game game); public abstract Watcher copy(); + } diff --git a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java index d78f382b523..7e96ee21ea8 100644 --- a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java @@ -50,7 +50,7 @@ public class CastSpellLastTurnWatcher extends Watcher { private final List spellsCastThisTurnInOrder = new ArrayList<>(); public CastSpellLastTurnWatcher() { - super("CastSpellLastTurnWatcher", WatcherScope.GAME); + super(CastSpellLastTurnWatcher.class.getName(), WatcherScope.GAME); } public CastSpellLastTurnWatcher(final CastSpellLastTurnWatcher watcher) { diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java new file mode 100644 index 00000000000..f1a1e49dd28 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java @@ -0,0 +1,65 @@ +/* + * 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.watchers.common; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +/** + * + * @author LevelX2 + */ +public class PermanentsEnteredBattlefieldWatcher extends Watcher { + + private final HashMap> enteringBattlefield = new HashMap<>(); + + public PermanentsEnteredBattlefieldWatcher() { + super(PermanentsEnteredBattlefieldWatcher.class.getName(), WatcherScope.GAME); + } + + public PermanentsEnteredBattlefieldWatcher(final PermanentsEnteredBattlefieldWatcher watcher) { + super(watcher); + } + + @Override + public PermanentsEnteredBattlefieldWatcher copy() { + return new PermanentsEnteredBattlefieldWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + Permanent perm = game.getPermanentEntering(event.getTargetId()); + if (perm != null) { + List permanents; + if (!enteringBattlefield.containsKey(perm.getControllerId())) { + permanents = new ArrayList<>(); + enteringBattlefield.put(perm.getControllerId(), permanents); + } else { + permanents = enteringBattlefield.get(perm.getControllerId()); + } + permanents.add(perm.copy()); // copy needed because attributes like color could be changed later + } + } + } + + @Override + public void reset() { + super.reset(); + enteringBattlefield.clear(); + } + + public List getThisTurnEnteringPermanents(UUID playerId) { + return enteringBattlefield.get(playerId); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java new file mode 100644 index 00000000000..c23e3747f0e --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -0,0 +1,74 @@ +/* + * 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.watchers.common; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import mage.MageObject; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +/** + * + * @author LevelX2 + */ +public class SpellsCastWatcher extends Watcher { + + private final HashMap> spellsCast = new HashMap<>(); + + public SpellsCastWatcher() { + super(SpellsCastWatcher.class.getName(), WatcherScope.GAME); + } + + public SpellsCastWatcher(final SpellsCastWatcher watcher) { + super(watcher); + } + + @Override + public SpellsCastWatcher copy() { + return new SpellsCastWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (EventType.SPELL_CAST.equals(event.getType())) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null) { + MageObject mageObject = game.getLastKnownInformation(event.getTargetId(), Zone.STACK); + if (mageObject instanceof Spell) { + spell = (Spell) mageObject; + } + } + if (spell != null) { + List spells; + if (!spellsCast.containsKey(spell.getControllerId())) { + spells = new ArrayList<>(); + spellsCast.put(spell.getControllerId(), spells); + } else { + spells = spellsCast.get(spell.getControllerId()); + } + spells.add(spell.copy()); // copy needed because attributes like color could be changed later + } + } + } + + @Override + public void reset() { + super.reset(); + spellsCast.clear(); + } + + public List getSpellsCastThisTurn(UUID playerId) { + return spellsCast.get(playerId); + } +}