From 5fbdca0b28a7b02e975f97b4f4887a4f1bd00a54 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:17:22 +0200 Subject: [PATCH 01/48] Fixed blocker selection bug The prior version didn't allow you to pick blockers if you weren't the attacking player (for example, in an EDH game if you activate this during another players turn and you're not the defender) --- Mage.Sets/src/mage/cards/b/BrutalHordechief.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java index 9add26359b4..3311459c095 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java +++ b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java @@ -151,7 +151,11 @@ class BrutalHordechiefReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()); + Player blockController = game.getPlayer(source.getControllerId()); + if (blockController != null) { + return true; + } + return false; } @Override From f5569f9c0a0b53472a713c62a0e3178ce4642164 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:20:14 +0200 Subject: [PATCH 02/48] Implemented Master Warcraft There's still line 190 left to fix... --- .../src/mage/cards/m/MasterWarcraft.java | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MasterWarcraft.java diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java new file mode 100644 index 00000000000..4ad9242b8a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -0,0 +1,249 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.RequirementEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; +import mage.abilities.effects.common.combat.CantAttackTargetEffect; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public class MasterWarcraft extends CardImpl { + + public MasterWarcraft(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R/W}{R/W}"); + + // Cast Master Warcraft only before attackers are declared. + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance)); + + // You choose which creatures attack this turn. + this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect().setText("You choose which creatures attack this turn.")); + + // You choose which creatures block this turn and how those creatures block. + this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect().setText("You choose which creatures block this turn and how those creatures block.")); + } + + public MasterWarcraft(final MasterWarcraft card) { + super(card); + } + + @Override + public MasterWarcraft copy() { + return new MasterWarcraft(this); + } +} + +class MasterWarcraftChooseAttackersEffect extends ReplacementEffectImpl { + + public MasterWarcraftChooseAttackersEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + this.staticText = "You choose which creatures attack this turn."; + } + + public MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) { + super(effect); + } + + @Override + public MasterWarcraftChooseAttackersEffect copy() { + return new MasterWarcraftChooseAttackersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player chooser = game.getPlayer(source.getControllerId()); + if (chooser != null) { + new MasterWarcraftAttackEffect().apply(game, source); // Master Warcraft imposes its effect right before the attackers being declared... + } + return false; // ...and then resumes the attack declaration + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARING_ATTACKERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Player chooser = game.getPlayer(source.getControllerId()); + Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId()); + if (chooser != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) { + return true; + } + return false; + } +} + +class MasterWarcraftAttackEffect extends OneShotEffect { + + MasterWarcraftAttackEffect() { + super(Outcome.Benefit); + } + + MasterWarcraftAttackEffect(final MasterWarcraftAttackEffect effect) { + super(effect); + } + + @Override + public MasterWarcraftAttackEffect copy() { + return new MasterWarcraftAttackEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"), true); + if (target.choose(Outcome.Neutral, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { + if (target.getTargets().contains(permanent.getId())) { + RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); + effect.setText(""); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } else { + RestrictionEffect effect = new MasterWarcraftCantAttackRestrictionEffect(); + effect.setText(""); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } + } + return true; + } + } + return false; + } +} + +class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { + + MasterWarcraftCantAttackRestrictionEffect() { + super(Duration.EndOfCombat); + } + + MasterWarcraftCantAttackRestrictionEffect(final MasterWarcraftCantAttackRestrictionEffect effect) { + super(effect); + } + + @Override + public MasterWarcraftCantAttackRestrictionEffect copy() { + return new MasterWarcraftCantAttackRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + // TODO: Make Master Warcraft still respect "This must attack if able" clauses + return this.getTargetPointer().getFirst(game, source).equals(permanent.getId()); + } + + @Override + public boolean canAttack(Game game) { + return false; + } + + @Override + public String getText(Mode mode) { + return "Unless {this} must attack, {this} can't attack."; + } +} + +class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { + + public MasterWarcraftChooseBlockersEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "You choose which creatures block this turn and how those creatures block."; + } + + public MasterWarcraftChooseBlockersEffect(final MasterWarcraftChooseBlockersEffect effect) { + super(effect); + } + + @Override + public MasterWarcraftChooseBlockersEffect copy() { + return new MasterWarcraftChooseBlockersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Player blockController = game.getPlayer(source.getControllerId()); + if (blockController != null) { + return true; + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player blockController = game.getPlayer(source.getControllerId()); + if (blockController != null) { + game.getCombat().selectBlockers(blockController, game); + return true; + } + return false; + } +} From 9563ea84be4ea631882deddf13c898d8ab31086f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:21:34 +0200 Subject: [PATCH 03/48] Implemented Master Warcraft --- Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java b/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java index 8bea79044a2..f2884e0aa52 100644 --- a/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java +++ b/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java @@ -213,6 +213,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet { cards.add(new SetCardInfo("Loxodon Hierarch", 214, Rarity.RARE, mage.cards.l.LoxodonHierarch.class)); cards.add(new SetCardInfo("Lurking Informant", 249, Rarity.COMMON, mage.cards.l.LurkingInformant.class)); cards.add(new SetCardInfo("Mark of Eviction", 58, Rarity.UNCOMMON, mage.cards.m.MarkOfEviction.class)); + cards.add(new SetCardInfo("Master Warcraft", 250, Rarity.RARE, mage.cards.m.MasterWarcraft.class)); cards.add(new SetCardInfo("Mausoleum Turnkey", 94, Rarity.UNCOMMON, mage.cards.m.MausoleumTurnkey.class)); cards.add(new SetCardInfo("Mindleech Mass", 215, Rarity.RARE, mage.cards.m.MindleechMass.class)); cards.add(new SetCardInfo("Mindmoil", 135, Rarity.RARE, mage.cards.m.Mindmoil.class)); From cc95002beeaa8a585f8b0ea844de116728ce1cfd Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:23:18 +0200 Subject: [PATCH 04/48] Implemented Master Warcraft --- Mage.Sets/src/mage/sets/Commander.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Commander.java b/Mage.Sets/src/mage/sets/Commander.java index a87566d11eb..3460d426797 100644 --- a/Mage.Sets/src/mage/sets/Commander.java +++ b/Mage.Sets/src/mage/sets/Commander.java @@ -210,6 +210,7 @@ public class Commander extends ExpansionSet { cards.add(new SetCardInfo("Malfegor", 208, Rarity.MYTHIC, mage.cards.m.Malfegor.class)); cards.add(new SetCardInfo("Mana-Charged Dragon", 129, Rarity.RARE, mage.cards.m.ManaChargedDragon.class)); cards.add(new SetCardInfo("Martyr's Bond", 19, Rarity.RARE, mage.cards.m.MartyrsBond.class)); + cards.add(new SetCardInfo("Master Warcraft", 209, Rarity.RARE, mage.cards.m.MasterWarcraft.class)); cards.add(new SetCardInfo("Memory Erosion", 50, Rarity.RARE, mage.cards.m.MemoryErosion.class)); cards.add(new SetCardInfo("Minds Aglow", 51, Rarity.RARE, mage.cards.m.MindsAglow.class)); cards.add(new SetCardInfo("Molten Slagheap", 282, Rarity.UNCOMMON, mage.cards.m.MoltenSlagheap.class)); From 5e25d77eda5cb5734cb5e68120de48300e89fd8e Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:23:26 +0200 Subject: [PATCH 05/48] Implemented Master Warcraft --- Mage.Sets/src/mage/sets/CommanderAnthology.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/CommanderAnthology.java b/Mage.Sets/src/mage/sets/CommanderAnthology.java index fc943136b22..7142cee09ae 100644 --- a/Mage.Sets/src/mage/sets/CommanderAnthology.java +++ b/Mage.Sets/src/mage/sets/CommanderAnthology.java @@ -207,6 +207,7 @@ public class CommanderAnthology extends ExpansionSet { cards.add(new SetCardInfo("Malfegor", 184, Rarity.MYTHIC, mage.cards.m.Malfegor.class)); cards.add(new SetCardInfo("Mana-Charged Dragon", 84, Rarity.RARE, mage.cards.m.ManaChargedDragon.class)); cards.add(new SetCardInfo("Masked Admirers", 127, Rarity.UNCOMMON, mage.cards.m.MaskedAdmirers.class)); + cards.add(new SetCardInfo("Master Warcraft", 202, Rarity.RARE, mage.cards.m.MasterWarcraft.class)); cards.add(new SetCardInfo("Mazirek, Kraul Death Priest", 185, Rarity.MYTHIC, mage.cards.m.MazirekKraulDeathPriest.class)); cards.add(new SetCardInfo("Meren of Clan Nel Toth", 186, Rarity.MYTHIC, mage.cards.m.MerenOfClanNelToth.class)); cards.add(new SetCardInfo("Mirror Entity", 16, Rarity.RARE, mage.cards.m.MirrorEntity.class)); From 56d93b0def77825c3f8edbc395e643de581ab0a6 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 16:41:15 +0200 Subject: [PATCH 06/48] Imports cleanup --- Mage.Sets/src/mage/cards/m/MasterWarcraft.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 4ad9242b8a0..6cbe123eacd 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -31,7 +31,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; @@ -39,8 +38,6 @@ import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; -import mage.abilities.effects.common.combat.CantAttackTargetEffect; -import mage.abilities.effects.common.combat.CantBlockTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; From 4155b30af33d66dfa0e39e5e8abf12129bc0378f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 23:20:27 +0200 Subject: [PATCH 07/48] Added missing "mustn't must attack" clause & some text fixes --- Mage.Sets/src/mage/cards/m/MasterWarcraft.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 6cbe123eacd..4c9cae66745 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -27,7 +27,7 @@ */ package mage.cards.m; -import java.util.UUID; +import java.util.*; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; @@ -64,10 +64,10 @@ public class MasterWarcraft extends CardImpl { this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance)); // You choose which creatures attack this turn. - this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect().setText("You choose which creatures attack this turn.")); + this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect()); // You choose which creatures block this turn and how those creatures block. - this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect().setText("You choose which creatures block this turn and how those creatures block.")); + this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect()); } public MasterWarcraft(final MasterWarcraft card) { @@ -84,7 +84,7 @@ class MasterWarcraftChooseAttackersEffect extends ReplacementEffectImpl { public MasterWarcraftChooseAttackersEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - this.staticText = "You choose which creatures attack this turn."; + this.staticText = "You choose which creatures attack this turn"; } public MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) { @@ -184,7 +184,12 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { - // TODO: Make Master Warcraft still respect "This must attack if able" clauses + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { + RequirementEffect effect = entry.getKey(); + if (effect.mustAttack(game)) { + return false; + } + } return this.getTargetPointer().getFirst(game, source).equals(permanent.getId()); } @@ -203,7 +208,7 @@ class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { public MasterWarcraftChooseBlockersEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "You choose which creatures block this turn and how those creatures block."; + staticText = "You choose which creatures block this turn and how those creatures block"; } public MasterWarcraftChooseBlockersEffect(final MasterWarcraftChooseBlockersEffect effect) { From 6400a401d4eac4744b324fccc16520701055fcf4 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 15 Oct 2017 23:26:11 +0200 Subject: [PATCH 08/48] Oversight fix --- Mage.Sets/src/mage/cards/m/MasterWarcraft.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 4c9cae66745..7216c62d059 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -183,14 +183,14 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } @Override - public boolean applies(Permanent permanent, Ability source, Game game) { + public boolean applies(Permanent creature, Ability source, Game game) { for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { RequirementEffect effect = entry.getKey(); if (effect.mustAttack(game)) { return false; } } - return this.getTargetPointer().getFirst(game, source).equals(permanent.getId()); + return this.getTargetPointer().getFirst(game, source).equals(creature.getId()); } @Override From e967c3951322f26cbd25ded5c489bdcbbccd2e62 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 15 Oct 2017 20:07:08 -0400 Subject: [PATCH 09/48] fixed Mana Maze not allowing colorless spells to be cast (fixes #4112) --- Mage.Sets/src/mage/cards/m/ManaMaze.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/ManaMaze.java b/Mage.Sets/src/mage/cards/m/ManaMaze.java index 97303002237..02038f0e618 100644 --- a/Mage.Sets/src/mage/cards/m/ManaMaze.java +++ b/Mage.Sets/src/mage/cards/m/ManaMaze.java @@ -53,11 +53,10 @@ import mage.watchers.Watcher; public class ManaMaze extends CardImpl { public ManaMaze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // Players can't cast spells that share a color with the spell most recently cast this turn. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaMazeEffect()), new LastSpellCastWatcher()); - } public ManaMaze(final ManaMaze card) { @@ -91,9 +90,8 @@ class ManaMazeEffect extends ContinuousRuleModifyingEffectImpl { Card card = game.getCard(event.getSourceId()); if (card != null) { LastSpellCastWatcher watcher = (LastSpellCastWatcher) game.getState().getWatchers().get(LastSpellCastWatcher.class.getSimpleName()); - if (watcher != null - && watcher.lastSpellCast != null) { - return card.getColor(game).contains(watcher.lastSpellCast.getColor(game)); + if (watcher != null && watcher.lastSpellCast != null) { + return !card.getColor(game).intersection(watcher.lastSpellCast.getColor(game)).isColorless(); } } return false; From f6c76026e80b8096b66f0db41e24b701b7317e6d Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 15 Oct 2017 20:15:32 -0400 Subject: [PATCH 10/48] fixed Gate to the Afterlife not triggering correctly --- Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java b/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java index bae5b782c84..1f1879647e3 100644 --- a/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java +++ b/Mage.Sets/src/mage/cards/g/GateToTheAfterlife.java @@ -29,7 +29,7 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -75,15 +75,17 @@ public class GateToTheAfterlife extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Whenever a nontoken creature you control dies, you gain 1 life. Then you may draw a card. If you do, discard a card. - Ability ability = new PutIntoGraveFromBattlefieldAllTriggeredAbility(new GainLifeEffect(1), false, filter, false, true); + Ability ability = new DiesCreatureTriggeredAbility(new GainLifeEffect(1), false, filter, false); Effect effect = new DrawDiscardControllerEffect(1, 1, true); effect.setText("Then you may draw a card. If you do, discard a card"); ability.addEffect(effect); this.addAbility(ability); // {2}, {T}, Sacrifice Gate to the Afterlife: Search your graveyard, hand, and/or library for a card named God-Pharaoh's Gift and put it onto the battlefield. If you seearch your library this way, shuffle it. Activate this ability only if there are six or more creature cards in your graveyard. - ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new GateToTheAfterlifeEffect(), new GenericManaCost(2), new CardsInControllerGraveCondition(6, new FilterCreatureCard())); + ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new GateToTheAfterlifeEffect(), new GenericManaCost(2), + new CardsInControllerGraveCondition(6, new FilterCreatureCard()) + ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); From 1c258c6b9f776e59efef7e3eaa7138fa9b4693ef Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 15 Oct 2017 20:48:26 -0400 Subject: [PATCH 11/48] fixed Lightmine Field damaging creatures which weren't declared as attackers (fixes #4111) --- .../src/mage/cards/l/LightmineField.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/l/LightmineField.java b/Mage.Sets/src/mage/cards/l/LightmineField.java index 2b4b8aaf723..7f9e081f62b 100644 --- a/Mage.Sets/src/mage/cards/l/LightmineField.java +++ b/Mage.Sets/src/mage/cards/l/LightmineField.java @@ -27,10 +27,15 @@ */ package mage.cards.l; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -49,7 +54,7 @@ import mage.game.permanent.Permanent; public class LightmineField extends CardImpl { public LightmineField(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // Whenever one or more creatures attack, Lightmine Field deals damage to each of those creatures equal to the number of attacking creatures. this.addAbility(new LightmineFieldTriggeredAbility()); @@ -87,7 +92,17 @@ class LightmineFieldTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return !game.getCombat().getAttackers().isEmpty(); + Set attackSet = new HashSet<>(); + for (UUID attackerId : game.getCombat().getAttackers()) { + Permanent attacker = game.getPermanent(attackerId); + if (attacker != null) { + attackSet.add(new MageObjectReference(attacker, game)); + } + } + for (Effect effect : getEffects()) { + effect.setValue("Lightmine Field", attackSet); + } + return !attackSet.isEmpty(); } @Override @@ -116,9 +131,11 @@ class LightmineFieldEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List attackers = game.getCombat().getAttackers(); int damage = attackers.size(); + Set attackSet = (Set) getValue("Lightmine Field"); if (!attackers.isEmpty()) { - for (UUID attacker : attackers) { - Permanent creature = game.getPermanent(attacker); + for (Iterator it = attackSet.iterator(); it.hasNext();) { + MageObjectReference attacker = it.next(); + Permanent creature = attacker.getPermanent(game); if (creature != null) { creature.damage(damage, source.getSourceId(), game, false, true); } From 587e8a75ef0e1df15f1d857231223bef3b9e2d54 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 17:04:04 -0400 Subject: [PATCH 12/48] fixed phased-out permanents not being invalid targets upon resolution --- Mage/src/main/java/mage/filter/FilterPermanent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/filter/FilterPermanent.java b/Mage/src/main/java/mage/filter/FilterPermanent.java index 287c4df4452..1f4acaa17ac 100644 --- a/Mage/src/main/java/mage/filter/FilterPermanent.java +++ b/Mage/src/main/java/mage/filter/FilterPermanent.java @@ -73,7 +73,7 @@ public class FilterPermanent extends FilterObject implements FilterIn @Override public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - if (!this.match(permanent, game)) { + if (!permanent.isPhasedIn() || !this.match(permanent, game)) { return false; } From 457269cb1205914ec8a28a4bab7f702d2a5b3f91 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 18:12:57 -0400 Subject: [PATCH 13/48] fixed some range of influence issues --- Mage.Sets/src/mage/cards/e/EnsnaringBridge.java | 10 +++++----- Mage.Sets/src/mage/cards/p/PriceOfGlory.java | 16 ++++++++++------ Mage/src/main/java/mage/players/PlayerImpl.java | 7 ++++--- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EnsnaringBridge.java b/Mage.Sets/src/mage/cards/e/EnsnaringBridge.java index ba74af82a29..d0d676f67fd 100644 --- a/Mage.Sets/src/mage/cards/e/EnsnaringBridge.java +++ b/Mage.Sets/src/mage/cards/e/EnsnaringBridge.java @@ -47,7 +47,7 @@ import mage.players.Player; public class EnsnaringBridge extends CardImpl { public EnsnaringBridge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Creatures with power greater than the number of cards in your hand can't attack. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EnsnaringBridgeRestrictionEffect())); @@ -63,7 +63,6 @@ public class EnsnaringBridge extends CardImpl { } } - class EnsnaringBridgeRestrictionEffect extends RestrictionEffect { public EnsnaringBridgeRestrictionEffect() { @@ -78,10 +77,11 @@ class EnsnaringBridgeRestrictionEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - return permanent.getPower().getValue() > controller.getHand().size(); + if (controller == null) { + return false; } - return false; + return controller.getInRange().contains(permanent.getControllerId()) + && permanent.getPower().getValue() > controller.getHand().size(); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PriceOfGlory.java b/Mage.Sets/src/mage/cards/p/PriceOfGlory.java index 35e30e70bfa..8f039fcdad6 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfGlory.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfGlory.java @@ -50,8 +50,7 @@ import mage.target.targetpointer.FixedTarget; public class PriceOfGlory extends CardImpl { public PriceOfGlory(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Whenever a player taps a land for mana, if it's not that player's turn, destroy that land. this.addAbility(new PriceOfGloryAbility()); @@ -86,11 +85,16 @@ class PriceOfGloryAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getSourceId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(event.getSourceId(), Zone.BATTLEFIELD); + return false; } - if (permanent != null && permanent.isLand() + Player player = game.getPlayer(controllerId); + if (player == null) { + return false; + } + if (permanent.isLand() + && player.getInRange().contains(permanent.getControllerId()) && !permanent.getControllerId().equals(game.getActivePlayerId())) { // intervening if clause getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); return true; @@ -125,7 +129,7 @@ class PriceOfGloryEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Permanent land = game.getPermanentOrLKIBattlefield(this.targetPointer.getFirst(game, source)); - if (land != null && !land.getControllerId().equals(game.getActivePlayerId())) { // intervening if clause has to be checked again + if (land != null && !land.getControllerId().equals(game.getActivePlayerId())) { // intervening if clause has to be checked again land.destroy(source.getSourceId(), game, false); } return true; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 1567246fdae..dac2bddf9a8 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3484,9 +3484,10 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean hasOpponent(UUID playerToCheckId, Game game - ) { - return !this.getId().equals(playerToCheckId) && game.isOpponent(this, playerToCheckId); + public boolean hasOpponent(UUID playerToCheckId, Game game) { + return !this.getId().equals(playerToCheckId) + && game.isOpponent(this, playerToCheckId) + && getInRange().contains(playerToCheckId); } @Override From 6605c212e4c3237772dac7c6c6e8a9ee308c6281 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 17 Oct 2017 02:30:12 +0400 Subject: [PATCH 14/48] Update AjaniTest.java typo comment --- .../test/java/org/mage/test/cards/planeswalker/AjaniTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/AjaniTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/AjaniTest.java index 7e0b24aa513..6a341d7b9b0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/AjaniTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/AjaniTest.java @@ -81,7 +81,7 @@ public class AjaniTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Kor Ally", 2); assertPermanentCount(playerA, "Oath of Gideon", 1); assertPermanentCount(playerA, "Ajani Goldmane", 1); - assertCounterCount("Ajani Goldmane", CounterType.LOYALTY, 6); // 5 + 1 = 5 + assertCounterCount("Ajani Goldmane", CounterType.LOYALTY, 6); // 4 + 1 + 1 = 6 assertLife(playerA, 22); assertLife(playerB, 20); From e65d3429059741ac6412856b41e4ee8f9700fb30 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 18:29:35 -0400 Subject: [PATCH 15/48] Implemented Imperial Edict --- Mage.Sets/src/mage/cards/i/ImperialEdict.java | 106 ++++++++++++++++++ .../src/mage/sets/PortalThreeKingdoms.java | 1 + 2 files changed, 107 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/ImperialEdict.java diff --git a/Mage.Sets/src/mage/cards/i/ImperialEdict.java b/Mage.Sets/src/mage/cards/i/ImperialEdict.java new file mode 100644 index 00000000000..90179d846be --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperialEdict.java @@ -0,0 +1,106 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.i; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 + */ +public class ImperialEdict extends CardImpl { + + public ImperialEdict(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Target opponent chooses a creature he or she controls. Destroy it. + this.getSpellAbility().addEffect(new ImperialEdictEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public ImperialEdict(final ImperialEdict card) { + super(card); + } + + @Override + public ImperialEdict copy() { + return new ImperialEdict(this); + } +} + +class ImperialEdictEffect extends OneShotEffect { + + ImperialEdictEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent chooses a creature he or she controls. Destroy it."; + } + + ImperialEdictEffect(final ImperialEdictEffect effect) { + super(effect); + } + + @Override + public ImperialEdictEffect copy() { + return new ImperialEdictEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index 4970599957a..7721b878430 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -100,6 +100,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Huang Zhong, Shu General", 8, Rarity.RARE, mage.cards.h.HuangZhongShuGeneral.class)); cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 137, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); cards.add(new SetCardInfo("Hunting Cheetah", 138, Rarity.UNCOMMON, mage.cards.h.HuntingCheetah.class)); + cards.add(new SetCardInfo("Imperial Edict", 77, Rarity.COMMON, mage.cards.i.ImperialEdict.class)); cards.add(new SetCardInfo("Imperial Recruiter", 113, Rarity.UNCOMMON, mage.cards.i.ImperialRecruiter.class)); cards.add(new SetCardInfo("Imperial Seal", 78, Rarity.RARE, mage.cards.i.ImperialSeal.class)); cards.add(new SetCardInfo("Independent Troops", 114, Rarity.COMMON, mage.cards.i.IndependentTroops.class)); From 0f4a61e737c59dce4fa431566d67324e13718930 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 18:32:29 -0400 Subject: [PATCH 16/48] Implemented Wei Assassins --- Mage.Sets/src/mage/cards/w/WeiAssassins.java | 116 ++++++++++++++++++ .../src/mage/sets/PortalThreeKingdoms.java | 1 + 2 files changed, 117 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WeiAssassins.java diff --git a/Mage.Sets/src/mage/cards/w/WeiAssassins.java b/Mage.Sets/src/mage/cards/w/WeiAssassins.java new file mode 100644 index 00000000000..282f9b0dc0f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeiAssassins.java @@ -0,0 +1,116 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 + */ +public class WeiAssassins extends CardImpl { + + public WeiAssassins(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Wei Assassins enters the battlefield, target opponent chooses a creature he or she controls. Destroy it. + Ability ability = new EntersBattlefieldTriggeredAbility(new WeiAssassinsEffect(), false); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + public WeiAssassins(final WeiAssassins card) { + super(card); + } + + @Override + public WeiAssassins copy() { + return new WeiAssassins(this); + } +} + +class WeiAssassinsEffect extends OneShotEffect { + + WeiAssassinsEffect() { + super(Outcome.Benefit); + this.staticText = "target opponent chooses a creature he or she controls. Destroy it."; + } + + WeiAssassinsEffect(final WeiAssassinsEffect effect) { + super(effect); + } + + @Override + public WeiAssassinsEffect copy() { + return new WeiAssassinsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index 7721b878430..2705ebde528 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -183,6 +183,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Volunteer Militia", 30, Rarity.COMMON, mage.cards.v.VolunteerMilitia.class)); cards.add(new SetCardInfo("Warrior's Oath", 124, Rarity.RARE, mage.cards.w.WarriorsOath.class)); cards.add(new SetCardInfo("Wei Ambush Force", 85, Rarity.COMMON, mage.cards.w.WeiAmbushForce.class)); + cards.add(new SetCardInfo("Wei Assassins", 86, Rarity.UNCOMMON, mage.cards.w.WeiAssassins.class)); cards.add(new SetCardInfo("Wei Elite Companions", 87, Rarity.UNCOMMON, mage.cards.w.WeiEliteCompanions.class)); cards.add(new SetCardInfo("Wei Infantry", 88, Rarity.COMMON, mage.cards.w.WeiInfantry.class)); cards.add(new SetCardInfo("Wei Night Raiders", 89, Rarity.UNCOMMON, mage.cards.w.WeiNightRaiders.class)); From ba31763a7ad6da933a441f6deb036cc4c5464720 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:05:39 -0400 Subject: [PATCH 17/48] Implemented Rally the Troops --- .../src/mage/cards/r/RallyTheTroops.java | 72 ++++++++++++++++ Mage.Sets/src/mage/sets/PortalSecondAge.java | 1 + .../src/mage/sets/PortalThreeKingdoms.java | 1 + .../common/AttackedThisStepCondition.java | 52 ++++++++++++ .../common/PlayerAttackedStepWatcher.java | 82 +++++++++++++++++++ 5 files changed, 208 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RallyTheTroops.java create mode 100644 Mage/src/main/java/mage/abilities/condition/common/AttackedThisStepCondition.java create mode 100644 Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java diff --git a/Mage.Sets/src/mage/cards/r/RallyTheTroops.java b/Mage.Sets/src/mage/cards/r/RallyTheTroops.java new file mode 100644 index 00000000000..4974b7b8065 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RallyTheTroops.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.StaticFilters; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class RallyTheTroops extends CardImpl { + + public RallyTheTroops(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Cast Rally the Troops only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Untap all creatures you control. + this.getSpellAbility().addEffect(new UntapAllControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES)); + } + + public RallyTheTroops(final RallyTheTroops card) { + super(card); + } + + @Override + public RallyTheTroops copy() { + return new RallyTheTroops(this); + } +} diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index 16e6adfa714..b73ec75ef03 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -165,6 +165,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Raiding Nightstalker", 24, Rarity.COMMON, mage.cards.r.RaidingNightstalker.class)); cards.add(new SetCardInfo("Rain of Daggers", 25, Rarity.RARE, mage.cards.r.RainOfDaggers.class)); cards.add(new SetCardInfo("Raise Dead", 26, Rarity.COMMON, mage.cards.r.RaiseDead.class)); + cards.add(new SetCardInfo("Rally the Troops", 139, Rarity.UNCOMMON, mage.cards.r.RallyTheTroops.class)); cards.add(new SetCardInfo("Ravenous Rats", 27, Rarity.COMMON, mage.cards.r.RavenousRats.class)); cards.add(new SetCardInfo("Razorclaw Bear", 82, Rarity.RARE, mage.cards.r.RazorclawBear.class)); cards.add(new SetCardInfo("Renewing Touch", 83, Rarity.UNCOMMON, mage.cards.r.RenewingTouch.class)); diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index 2705ebde528..89c183628dc 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -135,6 +135,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Plains", 168, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Poison Arrow", 80, Rarity.UNCOMMON, mage.cards.p.PoisonArrow.class)); cards.add(new SetCardInfo("Preemptive Strike", 50, Rarity.COMMON, mage.cards.p.PreemptiveStrike.class)); + cards.add(new SetCardInfo("Rally the Troops", 16, Rarity.UNCOMMON, mage.cards.r.RallyTheTroops.class)); cards.add(new SetCardInfo("Ravages of War", 17, Rarity.RARE, mage.cards.r.RavagesOfWar.class)); cards.add(new SetCardInfo("Ravaging Horde", 118, Rarity.UNCOMMON, mage.cards.r.RavagingHorde.class)); cards.add(new SetCardInfo("Red Cliffs Armada", 51, Rarity.UNCOMMON, mage.cards.r.RedCliffsArmada.class)); diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedThisStepCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisStepCondition.java new file mode 100644 index 00000000000..7ecc8fb53c7 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisStepCondition.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * @author LevelX2 + */ +public enum AttackedThisStepCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + PlayerAttackedStepWatcher watcher = (PlayerAttackedStepWatcher) game.getState().getWatchers().get(PlayerAttackedStepWatcher.class.getSimpleName()); + return watcher != null + && watcher.getNumberAttackingCurrentStep(source.getControllerId()) > 0; + } + + public String toString() { + return "during the declare attackers step and only if you've been attacked this step."; + } +} diff --git a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java new file mode 100644 index 00000000000..03f56c9beb5 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.watchers.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +/** + * @author LevelX2 + */ +public class PlayerAttackedStepWatcher extends Watcher { + + // With how many creatures attacked this player this turn + private final Map playerAttacked = new HashMap<>(); + + public PlayerAttackedStepWatcher() { + super(PlayerAttackedWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public PlayerAttackedStepWatcher(final PlayerAttackedStepWatcher watcher) { + super(watcher); + for (Map.Entry entry : watcher.playerAttacked.entrySet()) { + this.playerAttacked.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public PlayerAttackedStepWatcher copy() { + return new PlayerAttackedStepWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST) { + playerAttacked.clear(); + } + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + playerAttacked.putIfAbsent(event.getTargetId(), 0); + playerAttacked.compute(event.getTargetId(), (p, amount) -> amount + 1); + } + } + + @Override + public void reset() { + super.reset(); + playerAttacked.clear(); + } + + public int getNumberAttackingCurrentStep(UUID playerId) { + return playerAttacked.getOrDefault(playerId, 0); + } +} From ed8de87226c5a7474efe114426d385ec5cacb026 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:10:13 -0400 Subject: [PATCH 18/48] Implemented Eightfold Maze --- Mage.Sets/src/mage/cards/e/EightfoldMaze.java | 73 +++++++++++++++++++ .../src/mage/sets/MastersEditionIII.java | 1 + .../src/mage/sets/PortalThreeKingdoms.java | 1 + 3 files changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/e/EightfoldMaze.java diff --git a/Mage.Sets/src/mage/cards/e/EightfoldMaze.java b/Mage.Sets/src/mage/cards/e/EightfoldMaze.java new file mode 100644 index 00000000000..293c3b07929 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EightfoldMaze.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class EightfoldMaze extends CardImpl { + + public EightfoldMaze(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Cast Eightfold Maze only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Destroy target attacking creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public EightfoldMaze(final EightfoldMaze card) { + super(card); + } + + @Override + public EightfoldMaze copy() { + return new EightfoldMaze(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MastersEditionIII.java b/Mage.Sets/src/mage/sets/MastersEditionIII.java index a46ec0ceb0b..a9a8ee82a1f 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIII.java @@ -103,6 +103,7 @@ public class MastersEditionIII extends ExpansionSet { cards.add(new SetCardInfo("Disharmony", 95, Rarity.UNCOMMON, mage.cards.d.Disharmony.class)); cards.add(new SetCardInfo("Divine Intervention", 8, Rarity.RARE, mage.cards.d.DivineIntervention.class)); cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 96, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); + cards.add(new SetCardInfo("Eightfold Maze", 9, Rarity.UNCOMMON, mage.cards.e.EightfoldMaze.class)); cards.add(new SetCardInfo("Elves of Deep Shadow", 116, Rarity.COMMON, mage.cards.e.ElvesOfDeepShadow.class)); cards.add(new SetCardInfo("Evil Presence", 64, Rarity.COMMON, mage.cards.e.EvilPresence.class)); cards.add(new SetCardInfo("Exorcist", 10, Rarity.UNCOMMON, mage.cards.e.Exorcist.class)); diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index 89c183628dc..416798faf9e 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -80,6 +80,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Desperate Charge", 74, Rarity.UNCOMMON, mage.cards.d.DesperateCharge.class)); cards.add(new SetCardInfo("Diaochan, Artful Beauty", 108, Rarity.RARE, mage.cards.d.DiaochanArtfulBeauty.class)); cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 109, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); + cards.add(new SetCardInfo("Eightfold Maze", 2, Rarity.RARE, mage.cards.e.EightfoldMaze.class)); cards.add(new SetCardInfo("Empty City Ruse", 3, Rarity.UNCOMMON, mage.cards.e.EmptyCityRuse.class)); cards.add(new SetCardInfo("Exhaustion", 42, Rarity.RARE, mage.cards.e.Exhaustion.class)); cards.add(new SetCardInfo("Extinguish", 43, Rarity.COMMON, mage.cards.e.Extinguish.class)); From 5dc58c23ed29022b13637c3f4ab10391462c7e99 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:13:03 -0400 Subject: [PATCH 19/48] Implemented Champion's Victory --- .../src/mage/cards/c/ChampionsVictory.java | 73 +++++++++++++++++++ .../src/mage/sets/PortalThreeKingdoms.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/ChampionsVictory.java diff --git a/Mage.Sets/src/mage/cards/c/ChampionsVictory.java b/Mage.Sets/src/mage/cards/c/ChampionsVictory.java new file mode 100644 index 00000000000..a3d2222ee2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChampionsVictory.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class ChampionsVictory extends CardImpl { + + public ChampionsVictory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Cast Champion's Victory only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Return target attacking creature to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public ChampionsVictory(final ChampionsVictory card) { + super(card); + } + + @Override + public ChampionsVictory copy() { + return new ChampionsVictory(this); + } +} diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index 416798faf9e..bc64a4afe95 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -68,6 +68,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Cao Cao, Lord of Wei", 68, Rarity.RARE, mage.cards.c.CaoCaoLordOfWei.class)); cards.add(new SetCardInfo("Cao Ren, Wei Commander", 69, Rarity.RARE, mage.cards.c.CaoRenWeiCommander.class)); cards.add(new SetCardInfo("Capture of Jingzhou", 38, Rarity.RARE, mage.cards.c.CaptureOfJingzhou.class)); + cards.add(new SetCardInfo("Champion's Victory", 39, Rarity.UNCOMMON, mage.cards.c.ChampionsVictory.class)); cards.add(new SetCardInfo("Coercion", 70, Rarity.UNCOMMON, mage.cards.c.Coercion.class)); cards.add(new SetCardInfo("Control of the Court", 105, Rarity.UNCOMMON, mage.cards.c.ControlOfTheCourt.class)); cards.add(new SetCardInfo("Corrupt Court Official", 71, Rarity.UNCOMMON, mage.cards.c.CorruptCourtOfficial.class)); From c624cf9e5e0c5a820793e51d10d1535e37c744c1 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:16:30 -0400 Subject: [PATCH 20/48] Implemented Assassin's Blade --- .../src/mage/cards/a/AssassinsBlade.java | 85 +++++++++++++++++++ Mage.Sets/src/mage/sets/Portal.java | 1 + 2 files changed, 86 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AssassinsBlade.java diff --git a/Mage.Sets/src/mage/cards/a/AssassinsBlade.java b/Mage.Sets/src/mage/cards/a/AssassinsBlade.java new file mode 100644 index 00000000000..88c4ebf61da --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AssassinsBlade.java @@ -0,0 +1,85 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.a; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class AssassinsBlade extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack attacking creature"); + + static { + filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + filter.add(new AttackingPredicate()); + } + + public AssassinsBlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Cast Assassin's Blade only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Destroy target nonblack attacking creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + } + + public AssassinsBlade(final AssassinsBlade card) { + super(card); + } + + @Override + public AssassinsBlade copy() { + return new AssassinsBlade(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index 7551940ca82..93a720e123f 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -71,6 +71,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Armageddon", 167, Rarity.RARE, mage.cards.a.Armageddon.class)); cards.add(new SetCardInfo("Armored Pegasus", 168, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); cards.add(new SetCardInfo("Arrogant Vampire", 1, Rarity.UNCOMMON, mage.cards.a.ArrogantVampire.class)); + cards.add(new SetCardInfo("Assassin's Blade", 2, Rarity.UNCOMMON, mage.cards.a.AssassinsBlade.class)); cards.add(new SetCardInfo("Balance of Power", 42, Rarity.RARE, mage.cards.b.BalanceOfPower.class)); cards.add(new SetCardInfo("Baleful Stare", 43, Rarity.UNCOMMON, mage.cards.b.BalefulStare.class)); cards.add(new SetCardInfo("Bee Sting", 83, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); From 940a28daf2cd0425e054d889acc907f7f6d6fbc1 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:19:53 -0400 Subject: [PATCH 21/48] Implemented Command of Unsummoning --- .../mage/cards/c/CommandOfUnsummoning.java | 74 +++++++++++++++++++ Mage.Sets/src/mage/sets/Portal.java | 1 + 2 files changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CommandOfUnsummoning.java diff --git a/Mage.Sets/src/mage/cards/c/CommandOfUnsummoning.java b/Mage.Sets/src/mage/cards/c/CommandOfUnsummoning.java new file mode 100644 index 00000000000..9ea9c2069f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CommandOfUnsummoning.java @@ -0,0 +1,74 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class CommandOfUnsummoning extends CardImpl { + + public CommandOfUnsummoning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Cast Command of Unsummoning only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Return one or two target attacking creatures to their owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect().setText("Return one or two target attacking creatures to their owner's hand.")); + this.getSpellAbility().addTarget(new TargetAttackingCreature(1, 2, new FilterAttackingCreature(), false)); + } + + public CommandOfUnsummoning(final CommandOfUnsummoning card) { + super(card); + } + + @Override + public CommandOfUnsummoning copy() { + return new CommandOfUnsummoning(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index 93a720e123f..9f7cc244ca8 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -95,6 +95,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Cloud Dragon", 46, Rarity.RARE, mage.cards.c.CloudDragon.class)); cards.add(new SetCardInfo("Cloud Pirates", 47, Rarity.COMMON, mage.cards.c.CloudPirates.class)); cards.add(new SetCardInfo("Cloud Spirit", 48, Rarity.UNCOMMON, mage.cards.c.CloudSpirit.class)); + cards.add(new SetCardInfo("Command of Unsummoning", 49, Rarity.UNCOMMON, mage.cards.c.CommandOfUnsummoning.class)); cards.add(new SetCardInfo("Coral Eel", 50, Rarity.COMMON, mage.cards.c.CoralEel.class)); cards.add(new SetCardInfo("Craven Giant", 126, Rarity.COMMON, mage.cards.c.CravenGiant.class)); cards.add(new SetCardInfo("Craven Knight", 7, Rarity.COMMON, mage.cards.c.CravenKnight.class)); From e7520967fda8b077aba0f6e6f8497d51093b9e75 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:22:38 -0400 Subject: [PATCH 22/48] Implemented Defiant Stand --- Mage.Sets/src/mage/cards/d/DefiantStand.java | 76 ++++++++++++++++++++ Mage.Sets/src/mage/sets/Portal.java | 1 + 2 files changed, 77 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DefiantStand.java diff --git a/Mage.Sets/src/mage/cards/d/DefiantStand.java b/Mage.Sets/src/mage/cards/d/DefiantStand.java new file mode 100644 index 00000000000..cc089e33032 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefiantStand.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.d; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class DefiantStand extends CardImpl { + + public DefiantStand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Cast Defiant Stand only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Target creature gets +1/+3 until end of turn. Untap that creature. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 3, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public DefiantStand(final DefiantStand card) { + super(card); + } + + @Override + public DefiantStand copy() { + return new DefiantStand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index 9f7cc244ca8..e39c63572d5 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -102,6 +102,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Cruel Bargain", 8, Rarity.RARE, mage.cards.c.CruelBargain.class)); cards.add(new SetCardInfo("Cruel Tutor", 9, Rarity.RARE, mage.cards.c.CruelTutor.class)); cards.add(new SetCardInfo("Deep-Sea Serpent", 52, Rarity.UNCOMMON, mage.cards.d.DeepSeaSerpent.class)); + cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); cards.add(new SetCardInfo("Deja Vu", 53, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Desert Drake", 127, Rarity.UNCOMMON, mage.cards.d.DesertDrake.class)); cards.add(new SetCardInfo("Devastation", 128, Rarity.RARE, mage.cards.d.Devastation.class)); From 433195efe833036793d1b61083b6f0b3a1d5737c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:25:26 -0400 Subject: [PATCH 23/48] Implemented Scorching Winds --- .../src/mage/cards/s/ScorchingWinds.java | 72 +++++++++++++++++++ Mage.Sets/src/mage/sets/Portal.java | 1 + 2 files changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/ScorchingWinds.java diff --git a/Mage.Sets/src/mage/cards/s/ScorchingWinds.java b/Mage.Sets/src/mage/cards/s/ScorchingWinds.java new file mode 100644 index 00000000000..3c5bd2a76c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScorchingWinds.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.DamageAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class ScorchingWinds extends CardImpl { + + public ScorchingWinds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // Cast Scorching Winds only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Scorching Winds deals 1 damage to each attacking creature. + this.getSpellAbility().addEffect(new DamageAllEffect(1, new FilterAttackingCreature())); + } + + public ScorchingWinds(final ScorchingWinds card) { + super(card); + } + + @Override + public ScorchingWinds copy() { + return new ScorchingWinds(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index e39c63572d5..e8d0ad48526 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -217,6 +217,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Sacred Knight", 186, Rarity.COMMON, mage.cards.s.SacredKnight.class)); cards.add(new SetCardInfo("Sacred Nectar", 187, Rarity.COMMON, mage.cards.s.SacredNectar.class)); cards.add(new SetCardInfo("Scorching Spear", 154, Rarity.COMMON, mage.cards.s.ScorchingSpear.class)); + cards.add(new SetCardInfo("Scorching Winds", 155, Rarity.UNCOMMON, mage.cards.s.ScorchingWinds.class)); cards.add(new SetCardInfo("Seasoned Marshal", 188, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); cards.add(new SetCardInfo("Serpent Assassin", 31, Rarity.RARE, mage.cards.s.SerpentAssassin.class)); cards.add(new SetCardInfo("Serpent Warrior", 32, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); From a8f2c07fa6394b2336e2041e13b0602d019a477c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:27:25 -0400 Subject: [PATCH 24/48] Implemented Treetop Defense --- .../src/mage/cards/t/TreetopDefense.java | 73 +++++++++++++++++++ Mage.Sets/src/mage/sets/Portal.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TreetopDefense.java diff --git a/Mage.Sets/src/mage/cards/t/TreetopDefense.java b/Mage.Sets/src/mage/cards/t/TreetopDefense.java new file mode 100644 index 00000000000..c144df4dd76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TreetopDefense.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class TreetopDefense extends CardImpl { + + public TreetopDefense(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Cast Treetop Defense only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Creatures you control gain reach until end of turn. + this.getSpellAbility().addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn)); + } + + public TreetopDefense(final TreetopDefense card) { + super(card); + } + + @Override + public TreetopDefense copy() { + return new TreetopDefense(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index e8d0ad48526..8c15c619ed3 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -252,6 +252,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Tidal Surge", 75, Rarity.COMMON, mage.cards.t.TidalSurge.class)); cards.add(new SetCardInfo("Time Ebb", 76, Rarity.COMMON, mage.cards.t.TimeEbb.class)); cards.add(new SetCardInfo("Touch of Brilliance", 77, Rarity.COMMON, mage.cards.t.TouchOfBrilliance.class)); + cards.add(new SetCardInfo("Treetop Defense", 116, Rarity.RARE, mage.cards.t.TreetopDefense.class)); cards.add(new SetCardInfo("Undying Beast", 36, Rarity.COMMON, mage.cards.u.UndyingBeast.class)); cards.add(new SetCardInfo("Untamed Wilds", 117, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); cards.add(new SetCardInfo("Valorous Charge", 196, Rarity.UNCOMMON, mage.cards.v.ValorousCharge.class)); From 837db99952eeca58fe380d7dcc5ec0016f85a01c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 16 Oct 2017 20:28:23 -0400 Subject: [PATCH 25/48] Implemented Remove --- Mage.Sets/src/mage/cards/r/Remove.java | 73 ++++++++++++++++++++ Mage.Sets/src/mage/sets/PortalSecondAge.java | 1 + 2 files changed, 74 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/Remove.java diff --git a/Mage.Sets/src/mage/cards/r/Remove.java b/Mage.Sets/src/mage/cards/r/Remove.java new file mode 100644 index 00000000000..fa859c91a13 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Remove.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class Remove extends CardImpl { + + public Remove(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Cast Remove only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Return target attacking creature to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public Remove(final Remove card) { + super(card); + } + + @Override + public Remove copy() { + return new Remove(this); + } +} diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index b73ec75ef03..ced4eb2baff 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -168,6 +168,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Rally the Troops", 139, Rarity.UNCOMMON, mage.cards.r.RallyTheTroops.class)); cards.add(new SetCardInfo("Ravenous Rats", 27, Rarity.COMMON, mage.cards.r.RavenousRats.class)); cards.add(new SetCardInfo("Razorclaw Bear", 82, Rarity.RARE, mage.cards.r.RazorclawBear.class)); + cards.add(new SetCardInfo("Remove", 43, Rarity.UNCOMMON, mage.cards.r.Remove.class)); cards.add(new SetCardInfo("Renewing Touch", 83, Rarity.UNCOMMON, mage.cards.r.RenewingTouch.class)); cards.add(new SetCardInfo("Return of the Nightstalkers", 28, Rarity.RARE, mage.cards.r.ReturnOfTheNightstalkers.class)); cards.add(new SetCardInfo("Righteous Charge", 140, Rarity.COMMON, mage.cards.r.RighteousCharge.class)); From e65eefe5ab89982dc651d02f99dac77b2d18f1ca Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 17 Oct 2017 17:01:10 +0200 Subject: [PATCH 26/48] Rewrote a lot of code ...to handle multiple conflicting instances of Master Warcraft (still yet to rewrite the blocker effect) --- .../src/mage/cards/m/MasterWarcraft.java | 86 +++++++++++++------ 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 7216c62d059..cfbf33e8c15 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -32,6 +32,7 @@ import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.RequirementEffect; @@ -58,7 +59,7 @@ import mage.target.targetpointer.FixedTarget; public class MasterWarcraft extends CardImpl { public MasterWarcraft(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R/W}{R/W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R/W}{R/W}"); // Cast Master Warcraft only before attackers are declared. this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance)); @@ -80,11 +81,11 @@ public class MasterWarcraft extends CardImpl { } } -class MasterWarcraftChooseAttackersEffect extends ReplacementEffectImpl { +class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectImpl { public MasterWarcraftChooseAttackersEffect() { - super(Duration.EndOfTurn, Outcome.Benefit); - this.staticText = "You choose which creatures attack this turn"; + super(Duration.EndOfTurn, Outcome.Benefit, false, false); + staticText = "You choose which creatures attack this turn"; } public MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) { @@ -101,15 +102,6 @@ class MasterWarcraftChooseAttackersEffect extends ReplacementEffectImpl { return true; } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player chooser = game.getPlayer(source.getControllerId()); - if (chooser != null) { - new MasterWarcraftAttackEffect().apply(game, source); // Master Warcraft imposes its effect right before the attackers being declared... - } - return false; // ...and then resumes the attack declaration - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DECLARING_ATTACKERS; @@ -120,9 +112,19 @@ class MasterWarcraftChooseAttackersEffect extends ReplacementEffectImpl { Player chooser = game.getPlayer(source.getControllerId()); Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId()); if (chooser != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) { - return true; + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { + // Clears previous instances of "should attack" effects + // ("shouldn't attack" effects don't need cleaning because MasterWarcraftMustAttackRequirementEffect overrides them) + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) { + RequirementEffect effect = entry.getKey(); + if (effect instanceof MasterWarcraftMustAttackRequirementEffect) { + effect.discard(); + } + } + } + new MasterWarcraftAttackEffect().apply(game, source); // Master Warcraft imposes its effect right before the attackers being declared... } - return false; + return false; // ...and then resumes the attack declaration for the active player as normal } } @@ -145,20 +147,28 @@ class MasterWarcraftAttackEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + // TODO: find a way to undo creature selection Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"), true); if (target.choose(Outcome.Neutral, source.getControllerId(), source.getSourceId(), game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { + + // Choose creatures that will be attacking this combat if (target.getTargets().contains(permanent.getId())) { - RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); + RequirementEffect effect = new MasterWarcraftMustAttackRequirementEffect(); effect.setText(""); effect.setTargetPointer(new FixedTarget(permanent.getId())); game.addEffect(effect, source); + // TODO: find a better way to report attackers to game log + // game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " should attack this combat if able"); + + // All other creatures can't attack } else { RestrictionEffect effect = new MasterWarcraftCantAttackRestrictionEffect(); effect.setText(""); effect.setTargetPointer(new FixedTarget(permanent.getId())); game.addEffect(effect, source); } + } return true; } @@ -167,6 +177,30 @@ class MasterWarcraftAttackEffect extends OneShotEffect { } } +class MasterWarcraftMustAttackRequirementEffect extends AttacksIfAbleTargetEffect { + + MasterWarcraftMustAttackRequirementEffect() { + super(Duration.EndOfCombat); + } + + MasterWarcraftMustAttackRequirementEffect(final MasterWarcraftMustAttackRequirementEffect effect) { + super(effect); + } + + @Override + public MasterWarcraftMustAttackRequirementEffect copy() { + return new MasterWarcraftMustAttackRequirementEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (discarded) { + return false; + } + return this.getTargetPointer().getTargets(game, source).contains(permanent.getId()); + } +} + class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { MasterWarcraftCantAttackRestrictionEffect() { @@ -183,18 +217,18 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } @Override - public boolean applies(Permanent creature, Ability source, Game game) { - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { - RequirementEffect effect = entry.getKey(); - if (effect.mustAttack(game)) { - return false; - } - } - return this.getTargetPointer().getFirst(game, source).equals(creature.getId()); + public boolean applies(Permanent permanent, Ability source, Game game) { + return this.getTargetPointer().getFirst(game, source).equals(permanent.getId()); } @Override - public boolean canAttack(Game game) { + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, false, game).entrySet()) { + RequirementEffect effect = entry.getKey(); + if (effect.mustAttack(game)) { + return true; + } + } return false; } @@ -204,7 +238,7 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } } -class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { +class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { // TODO: replace this with ContinuousRuleModifyingEffectImpl public MasterWarcraftChooseBlockersEffect() { super(Duration.EndOfTurn, Outcome.Benefit); From 474048bcbc1603a69782961a535669591c304d21 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 17 Oct 2017 14:02:47 -0400 Subject: [PATCH 27/48] fixed implementation of Confusion in the Ranks --- .../src/mage/cards/c/ConfusionInTheRanks.java | 63 ++++++++++--------- .../src/main/java/mage/target/TargetImpl.java | 10 +-- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ConfusionInTheRanks.java b/Mage.Sets/src/mage/cards/c/ConfusionInTheRanks.java index 9ce9bb67b4d..b22e6a0da9b 100644 --- a/Mage.Sets/src/mage/cards/c/ConfusionInTheRanks.java +++ b/Mage.Sets/src/mage/cards/c/ConfusionInTheRanks.java @@ -60,21 +60,25 @@ public class ConfusionInTheRanks extends CardImpl { filter.add(Predicates.or( new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE), - new CardTypePredicate(CardType.ENCHANTMENT))); + new CardTypePredicate(CardType.ENCHANTMENT) + )); } private final UUID originalId; public ConfusionInTheRanks(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); // Whenever an artifact, creature, or enchantment enters the battlefield, its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents. Ability ability = new EntersBattlefieldAllTriggeredAbility( Zone.BATTLEFIELD, - new ExchangeControlTargetEffect(Duration.EndOfGame, "its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents"), - filter, - false, - SetTargetPointer.PERMANENT, - null); + new ExchangeControlTargetEffect( + Duration.EndOfGame, + "its controller chooses target permanent " + + "another player controls that shares a card type with it. " + + "Exchange control of those permanents" + ), + filter, false, SetTargetPointer.PERMANENT, null + ); ability.addTarget(new TargetPermanent()); originalId = ability.getOriginalId(); this.addAbility(ability); @@ -92,28 +96,31 @@ public class ConfusionInTheRanks extends CardImpl { for (Effect effect : ability.getEffects()) { enteringPermanentId = effect.getTargetPointer().getFirst(game, ability); } - if (enteringPermanentId != null) { - Permanent enteringPermanent = game.getPermanent(enteringPermanentId); - if (enteringPermanent != null) { - ability.setControllerId(enteringPermanent.getControllerId()); - ability.getTargets().clear(); - FilterPermanent filterTarget = new FilterPermanent(); - String message = ""; - filterTarget.add(Predicates.not(new ControllerIdPredicate(enteringPermanent.getControllerId()))); - Set cardTypesPredicates = new HashSet<>(1); - for (CardType cardTypeEntering : enteringPermanent.getCardType()) { - cardTypesPredicates.add(new CardTypePredicate(cardTypeEntering)); - if (!message.isEmpty()) { - message += "or "; - } - message += cardTypeEntering.toString().toLowerCase() + ' '; - } - filterTarget.add(Predicates.or(cardTypesPredicates)); - message += "you do not control"; - filterTarget.setMessage(message); - ability.getTargets().add(new TargetPermanent(filterTarget)); - } + if (enteringPermanentId == null) { + return; } + Permanent enteringPermanent = game.getPermanent(enteringPermanentId); + if (enteringPermanent == null) { + return; + } + ability.getTargets().clear(); + FilterPermanent filterTarget = new FilterPermanent(); + String message = ""; + filterTarget.add(Predicates.not(new ControllerIdPredicate(enteringPermanent.getControllerId()))); + Set cardTypesPredicates = new HashSet<>(1); + for (CardType cardTypeEntering : enteringPermanent.getCardType()) { + cardTypesPredicates.add(new CardTypePredicate(cardTypeEntering)); + if (!message.isEmpty()) { + message += "or "; + } + message += cardTypeEntering.toString().toLowerCase() + ' '; + } + filterTarget.add(Predicates.or(cardTypesPredicates)); + message += "you don't control"; + filterTarget.setMessage(message); + TargetPermanent target = new TargetPermanent(filterTarget); + target.setTargetController(enteringPermanent.getControllerId()); + ability.getTargets().add(target); } } diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 3dbe7568cc6..fb4aa7f6b87 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -128,11 +128,11 @@ public abstract class TargetImpl implements Target { @Override public String getMessage() { String suffix = ""; - if (targetController != null) { - // Hint for the selecting player that the targets must be valid from the point of the ability controller - // e.g. select opponent text may be misleading otherwise - suffix = " (target controlling!)"; - } +// if (targetController != null) { +// // Hint for the selecting player that the targets must be valid from the point of the ability controller +// // e.g. select opponent text may be misleading otherwise +// suffix = " (target controlling!)"; +// } if (getMaxNumberOfTargets() != 1) { StringBuilder sb = new StringBuilder(); sb.append("Select ").append(targetName); From 51221b2e3f1f8c0ae0b8fc1d8aaa3425624f69d7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 17 Oct 2017 15:32:31 -0400 Subject: [PATCH 28/48] Implemented Martyr's Cry --- Mage.Sets/src/mage/cards/m/MartyrsCry.java | 101 ++++++++++++++++++ Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + Mage.Sets/src/mage/sets/TheDark.java | 1 + 3 files changed, 103 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MartyrsCry.java diff --git a/Mage.Sets/src/mage/cards/m/MartyrsCry.java b/Mage.Sets/src/mage/cards/m/MartyrsCry.java new file mode 100644 index 00000000000..0441ab4f5b3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MartyrsCry.java @@ -0,0 +1,101 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public class MartyrsCry extends CardImpl { + + public MartyrsCry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{W}"); + + // Exile all white creatures. For each creature exiled this way, its controller draws a card. + this.getSpellAbility().addEffect(new MartyrsCryEffect()); + } + + public MartyrsCry(final MartyrsCry card) { + super(card); + } + + @Override + public MartyrsCry copy() { + return new MartyrsCry(this); + } +} + +class MartyrsCryEffect extends OneShotEffect { + + MartyrsCryEffect() { + super(Outcome.Exile); + this.staticText = "Exile all white creatures. For each creature exiled this way, its controller draws a card."; + } + + MartyrsCryEffect(final MartyrsCryEffect effect) { + super(effect); + } + + @Override + public MartyrsCryEffect copy() { + return new MartyrsCryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map playerCrtCount = new HashMap<>(); + for (Iterator it = game.getBattlefield().getActivePermanents(source.getControllerId(), game).iterator(); it.hasNext();) { + Permanent perm = it.next(); + if (perm != null && perm.isCreature() && perm.getColor(game).isWhite() && perm.moveToExile(null, null, source.getSourceId(), game)) { + playerCrtCount.putIfAbsent(perm.getControllerId(), 0); + playerCrtCount.compute(perm.getControllerId(), (p, amount) -> amount + 1); + } + } + for (UUID playerId : game.getPlayerList().toList()) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.drawCards(playerCrtCount.getOrDefault(playerId, 0), game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 25979eabb12..56844d6e8ed 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -186,6 +186,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Mahamoti Djinn", 52, Rarity.RARE, mage.cards.m.MahamotiDjinn.class)); cards.add(new SetCardInfo("Mana Matrix", 213, Rarity.RARE, mage.cards.m.ManaMatrix.class)); cards.add(new SetCardInfo("Mana Vault", 214, Rarity.RARE, mage.cards.m.ManaVault.class)); + cards.add(new SetCardInfo("Martyr's Cry", 19, Rarity.RARE, mage.cards.m.MartyrsCry.class)); cards.add(new SetCardInfo("Martyrs of Korlis", 20, Rarity.UNCOMMON, mage.cards.m.MartyrsOfKorlis.class)); cards.add(new SetCardInfo("Maze of Ith", 246, Rarity.RARE, mage.cards.m.MazeOfIth.class)); cards.add(new SetCardInfo("Mightstone", 215, Rarity.COMMON, mage.cards.m.Mightstone.class)); diff --git a/Mage.Sets/src/mage/sets/TheDark.java b/Mage.Sets/src/mage/sets/TheDark.java index bacad0e06f9..8f077c9ab87 100644 --- a/Mage.Sets/src/mage/sets/TheDark.java +++ b/Mage.Sets/src/mage/sets/TheDark.java @@ -105,6 +105,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Marsh Gas", 10, Rarity.COMMON, mage.cards.m.MarshGas.class)); cards.add(new SetCardInfo("Marsh Goblins", 118, Rarity.COMMON, mage.cards.m.MarshGoblins.class)); cards.add(new SetCardInfo("Marsh Viper", 44, Rarity.COMMON, mage.cards.m.MarshViper.class)); + cards.add(new SetCardInfo("Martyr's Cry", 85, Rarity.RARE, mage.cards.m.MartyrsCry.class)); cards.add(new SetCardInfo("Maze of Ith", 114, Rarity.UNCOMMON, mage.cards.m.MazeOfIth.class)); cards.add(new SetCardInfo("Merfolk Assassin", 31, Rarity.UNCOMMON, mage.cards.m.MerfolkAssassin.class)); cards.add(new SetCardInfo("Mind Bomb", 32, Rarity.RARE, mage.cards.m.MindBomb.class)); From 93c878c0d8911250b9956e9b103bffb8bfc87c92 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 17 Oct 2017 15:48:29 -0400 Subject: [PATCH 29/48] Implemented Krovikan Horror --- .../src/mage/cards/k/KrovikanHorror.java | 114 ++++++++++++++++++ Mage.Sets/src/mage/sets/Alliances.java | 1 + Mage.Sets/src/mage/sets/MastersEditionII.java | 1 + 3 files changed, 116 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KrovikanHorror.java diff --git a/Mage.Sets/src/mage/cards/k/KrovikanHorror.java b/Mage.Sets/src/mage/cards/k/KrovikanHorror.java new file mode 100644 index 00000000000..4b37bb97c6a --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KrovikanHorror.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.cards.Card; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author TheElk801 + */ +public class KrovikanHorror extends CardImpl { + + public KrovikanHorror(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HORROR); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of the end step, if Krovikan Horror is in your graveyard with a creature card directly above it, you may return Krovikan Horror to your hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), + TargetController.ANY, KrovikanHorrorCondition.instance, true + )); + + // {1}, Sacrifice a creature: Krovikan Horror deals 1 damage to target creature or player. + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new GenericManaCost(1)); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability); + } + + public KrovikanHorror(final KrovikanHorror card) { + super(card); + } + + @Override + public KrovikanHorror copy() { + return new KrovikanHorror(this); + } +} + +enum KrovikanHorrorCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + boolean nextCard = false; + for (Card card : controller.getGraveyard().getCards(game)) { + if (nextCard) { + return card.isCreature(); + } + if (card.getId().equals(source.getSourceId())) { + nextCard = true; + } + } + } + return false; + } + + @Override + public String toString() { + return "{this} is in your graveyard with a creature card directly above it"; + } +} diff --git a/Mage.Sets/src/mage/sets/Alliances.java b/Mage.Sets/src/mage/sets/Alliances.java index 6144a6e8bba..4147a070039 100644 --- a/Mage.Sets/src/mage/sets/Alliances.java +++ b/Mage.Sets/src/mage/sets/Alliances.java @@ -104,6 +104,7 @@ public class Alliances extends ExpansionSet { cards.add(new SetCardInfo("Keeper of Tresserhorn", 14, Rarity.RARE, mage.cards.k.KeeperOfTresserhorn.class)); cards.add(new SetCardInfo("Kjeldoran Home Guard", 135, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); cards.add(new SetCardInfo("Kjeldoran Outpost", 184, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); + cards.add(new SetCardInfo("Krovikan Horror", 15, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); cards.add(new SetCardInfo("Krovikan Plague", 16, Rarity.UNCOMMON, mage.cards.k.KrovikanPlague.class)); cards.add(new SetCardInfo("Lake of the Dead", 185, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); cards.add(new SetCardInfo("Library of Lat-Nam", 47, Rarity.RARE, mage.cards.l.LibraryOfLatNam.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionII.java b/Mage.Sets/src/mage/sets/MastersEditionII.java index 2b276c52b34..054b8bbb593 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionII.java @@ -169,6 +169,7 @@ public class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Kjeldoran Outpost", 233, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); cards.add(new SetCardInfo("Knight of Stromgald", 99, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); cards.add(new SetCardInfo("Krovikan Fetish", 100, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); + cards.add(new SetCardInfo("Krovikan Horror", 101, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); cards.add(new SetCardInfo("Krovikan Sorcerer", 51, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); cards.add(new SetCardInfo("Leaping Lizard", 171, Rarity.COMMON, mage.cards.l.LeapingLizard.class)); cards.add(new SetCardInfo("Lim-Dul's High Guard", 103, Rarity.UNCOMMON, LimDulsHighGuard.class)); From 36523d541540fff6947381442b10bae999b9c49a Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 10:40:21 -0400 Subject: [PATCH 30/48] text fix --- Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java b/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java index 3a12d76299c..0409f1141d5 100644 --- a/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java +++ b/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java @@ -48,7 +48,7 @@ import mage.target.common.TargetControlledCreaturePermanent; public class GlenElendraPranksters extends CardImpl { public GlenElendraPranksters(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); @@ -57,8 +57,10 @@ public class GlenElendraPranksters extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a spell during an opponent's turn, you may return target creature you control to its owner's hand. - Ability ability = new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandTargetEffect(), true), OnOpponentsTurnCondition.instance, - "Whenever you cast a spell during an opponent's turn, you may have target creature get -1/-1 until end of turn."); + Ability ability = new ConditionalTriggeredAbility( + new SpellCastControllerTriggeredAbility(new ReturnToHandTargetEffect(), true), OnOpponentsTurnCondition.instance, + "Whenever you cast a spell during an opponent's turn, you may return target creature you control to its owner's hand." + ); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } From 74642115397ef452128a7f15b8c16df952c98f23 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 14:26:35 -0400 Subject: [PATCH 31/48] Implemented Shyft --- Mage.Sets/src/mage/cards/s/Shyft.java | 96 +++++++++++++++++++ Mage.Sets/src/mage/sets/IceAge.java | 1 + Mage.Sets/src/mage/sets/MastersEditionII.java | 1 + 3 files changed, 98 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/Shyft.java diff --git a/Mage.Sets/src/mage/cards/s/Shyft.java b/Mage.Sets/src/mage/cards/s/Shyft.java new file mode 100644 index 00000000000..ea0e075411a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Shyft.java @@ -0,0 +1,96 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BecomesColorOrColorsTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.game.Game; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public class Shyft extends CardImpl { + + public Shyft(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SHAPESHIFTER); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // At the beginning of your upkeep, you may have Shyft become the color or colors of your choice. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ShyftEffect(), TargetController.YOU, true)); + } + + public Shyft(final Shyft card) { + super(card); + } + + @Override + public Shyft copy() { + return new Shyft(this); + } +} + +class ShyftEffect extends OneShotEffect { + + ShyftEffect() { + super(Outcome.Benefit); + this.staticText = "have {this} become the color or colors of your choice."; + } + + ShyftEffect(final ShyftEffect effect) { + super(effect); + } + + @Override + public ShyftEffect copy() { + return new ShyftEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Effect effect = new BecomesColorOrColorsTargetEffect(Duration.Custom); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + return effect.apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index 2f96059ac19..9e89571fa80 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -272,6 +272,7 @@ public class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Shambling Strider", 151, Rarity.COMMON, mage.cards.s.ShamblingStrider.class)); cards.add(new SetCardInfo("Shatter", 216, Rarity.COMMON, mage.cards.s.Shatter.class)); cards.add(new SetCardInfo("Shield of the Ages", 310, Rarity.UNCOMMON, mage.cards.s.ShieldOfTheAges.class)); + cards.add(new SetCardInfo("Shyft", 96, Rarity.RARE, mage.cards.s.Shyft.class)); cards.add(new SetCardInfo("Sibilant Spirit", 97, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); cards.add(new SetCardInfo("Silver Erne", 98, Rarity.UNCOMMON, mage.cards.s.SilverErne.class)); cards.add(new SetCardInfo("Skeleton Ship", 379, Rarity.RARE, mage.cards.s.SkeletonShip.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionII.java b/Mage.Sets/src/mage/sets/MastersEditionII.java index 054b8bbb593..768db1c538d 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionII.java @@ -216,6 +216,7 @@ public class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Sea Drake", 64, Rarity.RARE, mage.cards.s.SeaDrake.class)); cards.add(new SetCardInfo("Sea Spirit", 65, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); cards.add(new SetCardInfo("Shrink", 175, Rarity.COMMON, mage.cards.s.Shrink.class)); + cards.add(new SetCardInfo("Shyft", 66, Rarity.COMMON, mage.cards.s.Shyft.class)); cards.add(new SetCardInfo("Sibilant Spirit", 67, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); cards.add(new SetCardInfo("Skeleton Ship", 197, Rarity.RARE, mage.cards.s.SkeletonShip.class)); cards.add(new SetCardInfo("Skull Catapult", 219, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); From 08bdfcc961ab7f63db8f75bd6802863dece6f454 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 14:45:30 -0400 Subject: [PATCH 32/48] updated some text and implementations --- .../src/mage/cards/g/GiantTrapDoorSpider.java | 32 +------- Mage.Sets/src/mage/cards/h/HuntingKavu.java | 32 +------- Mage.Sets/src/mage/cards/s/SoulSnare.java | 28 +------ .../common/FilterCreatureAttackingYou.java | 82 +++++++++++++++++++ 4 files changed, 90 insertions(+), 84 deletions(-) create mode 100644 Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java diff --git a/Mage.Sets/src/mage/cards/g/GiantTrapDoorSpider.java b/Mage.Sets/src/mage/cards/g/GiantTrapDoorSpider.java index 6c0ed14e87a..968ce61a782 100644 --- a/Mage.Sets/src/mage/cards/g/GiantTrapDoorSpider.java +++ b/Mage.Sets/src/mage/cards/g/GiantTrapDoorSpider.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -42,11 +41,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.target.common.FilterCreatureAttackingYou; import mage.target.common.TargetCreaturePermanent; /** @@ -55,7 +52,7 @@ import mage.target.common.TargetCreaturePermanent; */ public class GiantTrapDoorSpider extends CardImpl { - private static final HuntingKavuFilter filter = new HuntingKavuFilter(); + private static final FilterCreatureAttackingYou filter = new FilterCreatureAttackingYou("creature without flying that's attacking you"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); @@ -71,7 +68,7 @@ public class GiantTrapDoorSpider extends CardImpl { // {1}{R}{G}, {tap}: Exile Giant Trap Door Spider and target creature without flying that's attacking you. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(), new ManaCostsImpl("{1}{R}{G}")); ability.addCost(new TapSourceCost()); - ability.addEffect(new ExileTargetEffect()); + ability.addEffect(new ExileTargetEffect().setText("and target creature without flying that's attacking you")); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } @@ -85,26 +82,3 @@ public class GiantTrapDoorSpider extends CardImpl { return new GiantTrapDoorSpider(this); } } - -class HuntingKavuFilter extends FilterAttackingCreature { - - public HuntingKavuFilter() { - super("creature without flying that's attacking you"); - } - - public HuntingKavuFilter(final HuntingKavuFilter filter) { - super(filter); - } - - @Override - public HuntingKavuFilter copy() { - return new HuntingKavuFilter(this); - } - - @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, sourceId, playerId, game) - && permanent.isAttacking() // to prevent unneccessary combat checking if not attacking - && playerId.equals(game.getCombat().getDefenderId(permanent.getId())); - } -} diff --git a/Mage.Sets/src/mage/cards/h/HuntingKavu.java b/Mage.Sets/src/mage/cards/h/HuntingKavu.java index c6541cbc9da..c4444d13389 100644 --- a/Mage.Sets/src/mage/cards/h/HuntingKavu.java +++ b/Mage.Sets/src/mage/cards/h/HuntingKavu.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -42,11 +41,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.target.common.FilterCreatureAttackingYou; import mage.target.common.TargetCreaturePermanent; /** @@ -55,7 +52,7 @@ import mage.target.common.TargetCreaturePermanent; */ public class HuntingKavu extends CardImpl { - private static final HuntingKavuFilter filter = new HuntingKavuFilter(); + private static final FilterCreatureAttackingYou filter = new FilterCreatureAttackingYou("creature without flying that's attacking you"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); @@ -71,7 +68,7 @@ public class HuntingKavu extends CardImpl { // {1}{R}{G}, {tap}: Exile Hunting Kavu and target creature without flying that's attacking you. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(), new ManaCostsImpl("{1}{R}{G}")); ability.addCost(new TapSourceCost()); - ability.addEffect(new ExileTargetEffect()); + ability.addEffect(new ExileTargetEffect().setText("nd target creature without flying that's attacking you")); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } @@ -85,26 +82,3 @@ public class HuntingKavu extends CardImpl { return new HuntingKavu(this); } } - -class HuntingKavuFilter extends FilterAttackingCreature { - - public HuntingKavuFilter() { - super("creature without flying that's attacking you"); - } - - public HuntingKavuFilter(final HuntingKavuFilter filter) { - super(filter); - } - - @Override - public HuntingKavuFilter copy() { - return new HuntingKavuFilter(this); - } - - @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, sourceId, playerId, game) - && permanent.isAttacking() // to prevent unneccessary combat checking if not attacking - && playerId.equals(game.getCombat().getDefenderId(permanent.getId())); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SoulSnare.java b/Mage.Sets/src/mage/cards/s/SoulSnare.java index e68f31e8fae..984e93f80a8 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSnare.java +++ b/Mage.Sets/src/mage/cards/s/SoulSnare.java @@ -37,9 +37,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.target.common.FilterCreatureAttackingYou; import mage.target.common.TargetCreaturePermanent; /** @@ -56,7 +54,7 @@ public class SoulSnare extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{W}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(new SoulSnareFilter())); + ability.addTarget(new TargetCreaturePermanent(new FilterCreatureAttackingYou(true))); this.addAbility(ability); } @@ -69,25 +67,3 @@ public class SoulSnare extends CardImpl { return new SoulSnare(this); } } - -class SoulSnareFilter extends FilterAttackingCreature { - - public SoulSnareFilter() { - super("creature that's attacking you or a planeswalker you control"); - } - - public SoulSnareFilter(final SoulSnareFilter filter) { - super(filter); - } - - @Override - public SoulSnareFilter copy() { - return new SoulSnareFilter(this); - } - - @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, sourceId, playerId, game) - && playerId.equals(game.getCombat().getDefendingPlayerId(permanent.getId(), game)); - } -} diff --git a/Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java b/Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java new file mode 100644 index 00000000000..308089f38aa --- /dev/null +++ b/Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java @@ -0,0 +1,82 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. + */ +package mage.target.common; + +import java.util.UUID; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author TheElk801 + */ +public class FilterCreatureAttackingYou extends FilterAttackingCreature { + + private final boolean orWalker; + + public FilterCreatureAttackingYou() { + this(false); + } + + public FilterCreatureAttackingYou(boolean orWalker) { + this("creature that's attacking you" + (orWalker ? "or a planeswalker you control" : ""), orWalker); + } + + public FilterCreatureAttackingYou(String name) { + this(name, false); + } + + public FilterCreatureAttackingYou(String name, boolean orWalker) { + super(name); + this.orWalker = orWalker; + } + + public FilterCreatureAttackingYou(final FilterCreatureAttackingYou filter) { + super(filter); + this.orWalker = filter.orWalker; + } + + @Override + public FilterCreatureAttackingYou copy() { + return new FilterCreatureAttackingYou(this); + } + + @Override + public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { + if (orWalker) { + return super.match(permanent, sourceId, playerId, game) + && permanent.isAttacking() // to prevent unneccessary combat checking if not attacking + && playerId.equals(game.getCombat().getDefendingPlayerId(permanent.getId(), game)); + } else { + return super.match(permanent, sourceId, playerId, game) + && permanent.isAttacking() // to prevent unneccessary combat checking if not attacking + && playerId.equals(game.getCombat().getDefenderId(permanent.getId())); + } + } +} From 8cbffc89d8e336a15e680e9e252a938502d18c39 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 14:53:30 -0400 Subject: [PATCH 33/48] updated some text and implementations --- .../{target => filter}/common/FilterCreatureAttackingYou.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Mage/src/main/java/mage/{target => filter}/common/FilterCreatureAttackingYou.java (100%) diff --git a/Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java b/Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java similarity index 100% rename from Mage/src/main/java/mage/target/common/FilterCreatureAttackingYou.java rename to Mage/src/main/java/mage/filter/common/FilterCreatureAttackingYou.java From bcc319a636b094ee982a2a554aadb23bf429aa5c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 14:57:17 -0400 Subject: [PATCH 34/48] Implemented Ice Floe --- Mage.Sets/src/mage/cards/i/IceFloe.java | 82 +++++++++++++++++++ Mage.Sets/src/mage/sets/FifthEdition.java | 1 + Mage.Sets/src/mage/sets/IceAge.java | 1 + Mage.Sets/src/mage/sets/MastersEditionII.java | 1 + 4 files changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/IceFloe.java diff --git a/Mage.Sets/src/mage/cards/i/IceFloe.java b/Mage.Sets/src/mage/cards/i/IceFloe.java new file mode 100644 index 00000000000..974b442ac5a --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IceFloe.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.i; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SkipUntapOptionalAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DontUntapAsLongAsSourceTappedEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.FilterCreatureAttackingYou; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author TheElk801 + */ +public class IceFloe extends CardImpl { + + private static final FilterCreatureAttackingYou filter = new FilterCreatureAttackingYou("creature without flying that's attacking you"); + + static { + filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } + + public IceFloe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // You may choose not to untap Ice Floe during your untap step. + this.addAbility(new SkipUntapOptionalAbility()); + + // {tap}: Tap target creature without flying that's attacking you. It doesn't untap during its controller's untap step for as long as Ice Floe remains tapped. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addEffect(new DontUntapAsLongAsSourceTappedEffect()); + this.addAbility(ability); + } + + public IceFloe(final IceFloe card) { + super(card); + } + + @Override + public IceFloe copy() { + return new IceFloe(this); + } +} diff --git a/Mage.Sets/src/mage/sets/FifthEdition.java b/Mage.Sets/src/mage/sets/FifthEdition.java index c04cc7dae8f..58f3d4975ca 100644 --- a/Mage.Sets/src/mage/sets/FifthEdition.java +++ b/Mage.Sets/src/mage/sets/FifthEdition.java @@ -217,6 +217,7 @@ public class FifthEdition extends ExpansionSet { cards.add(new SetCardInfo("Icatian Scout", 313, Rarity.COMMON, IcatianScout.class)); cards.add(new SetCardInfo("Icatian Store", 423, Rarity.RARE, mage.cards.i.IcatianStore.class)); cards.add(new SetCardInfo("Icatian Town", 314, Rarity.RARE, mage.cards.i.IcatianTown.class)); + cards.add(new SetCardInfo("Ice Floe", 424, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); cards.add(new SetCardInfo("Imposing Visage", 241, Rarity.COMMON, mage.cards.i.ImposingVisage.class)); cards.add(new SetCardInfo("Incinerate", 242, Rarity.COMMON, mage.cards.i.Incinerate.class)); cards.add(new SetCardInfo("Inferno", 243, Rarity.RARE, mage.cards.i.Inferno.class)); diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index 9e89571fa80..ae41e2d5b81 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -167,6 +167,7 @@ public class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Hyalopterous Lemure", 21, Rarity.UNCOMMON, mage.cards.h.HyalopterousLemure.class)); cards.add(new SetCardInfo("Hydroblast", 72, Rarity.COMMON, mage.cards.h.Hydroblast.class)); cards.add(new SetCardInfo("Hymn of Rebirth", 373, Rarity.UNCOMMON, mage.cards.h.HymnOfRebirth.class)); + cards.add(new SetCardInfo("Ice Floe", 333, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); cards.add(new SetCardInfo("Iceberg", 73, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); cards.add(new SetCardInfo("Icequake", 22, Rarity.UNCOMMON, mage.cards.i.Icequake.class)); cards.add(new SetCardInfo("Icy Manipulator", 297, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionII.java b/Mage.Sets/src/mage/sets/MastersEditionII.java index 768db1c538d..f93ecf7023e 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionII.java @@ -148,6 +148,7 @@ public class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Helm of Obedience", 210, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); cards.add(new SetCardInfo("Icatian Javelineers", 15, Rarity.COMMON, IcatianJavelineers.class)); cards.add(new SetCardInfo("Icatian Scout", 17, Rarity.COMMON, IcatianScout.class)); + cards.add(new SetCardInfo("Ice Floe", 232, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); cards.add(new SetCardInfo("Iceberg", 49, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); cards.add(new SetCardInfo("Icequake", 94, Rarity.COMMON, mage.cards.i.Icequake.class)); cards.add(new SetCardInfo("Icy Prison", 50, Rarity.COMMON, mage.cards.i.IcyPrison.class)); From 53f77ac9cd3a99a4afc15d10ca5fb71283b6b5db Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 15:02:11 -0400 Subject: [PATCH 35/48] some fixes --- Mage.Sets/src/mage/cards/s/SnowFortress.java | 33 +++---------------- .../src/mage/cards/s/StalkingLeonin.java | 26 ++------------- 2 files changed, 6 insertions(+), 53 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SnowFortress.java b/Mage.Sets/src/mage/cards/s/SnowFortress.java index 7ec925286be..1d58404c885 100644 --- a/Mage.Sets/src/mage/cards/s/SnowFortress.java +++ b/Mage.Sets/src/mage/cards/s/SnowFortress.java @@ -42,12 +42,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.common.TargetCreatureOrPlayer; +import mage.target.common.FilterCreatureAttackingYou; +import mage.target.common.TargetCreaturePermanent; /** * @@ -55,7 +53,7 @@ import mage.target.common.TargetCreatureOrPlayer; */ public class SnowFortress extends CardImpl { - private static final SnowFortressFilter filter = new SnowFortressFilter(); + private static final FilterCreatureAttackingYou filter = new FilterCreatureAttackingYou("creature without flying that's attacking you"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); @@ -79,7 +77,7 @@ public class SnowFortress extends CardImpl { // {3}: Snow Fortress deals 1 damage to target creature without flying that's attacking you. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new GenericManaCost(3)); - ability.addTarget(new TargetCreatureOrPlayer()); + ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } @@ -92,26 +90,3 @@ public class SnowFortress extends CardImpl { return new SnowFortress(this); } } - -class SnowFortressFilter extends FilterAttackingCreature { - - public SnowFortressFilter() { - super("creature without flying that's attacking you"); - } - - public SnowFortressFilter(final SnowFortressFilter filter) { - super(filter); - } - - @Override - public SnowFortressFilter copy() { - return new SnowFortressFilter(this); - } - - @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, sourceId, playerId, game) - && permanent.isAttacking() // to prevent unneccessary combat checking if not attacking - && playerId.equals(game.getCombat().getDefenderId(permanent.getId())); - } -} diff --git a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java index c917e94728c..a181a42116c 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java +++ b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java @@ -44,10 +44,10 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.common.FilterCreatureAttackingYou; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetOpponent; import mage.util.CardUtil; @@ -72,7 +72,7 @@ public class StalkingLeonin extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new StalkingLeoninChooseOpponent(), false)); // Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new StalkingLeoninEffect(), new StalkingLeoninRevealOpponentCost()); - ability.addTarget(new TargetCreaturePermanent(new StalkingLeoninFilter())); + ability.addTarget(new TargetCreaturePermanent(new FilterCreatureAttackingYou())); this.addAbility(ability); } @@ -217,25 +217,3 @@ class StalkingLeoninEffect extends OneShotEffect { return false; } } - -class StalkingLeoninFilter extends FilterAttackingCreature { - - public StalkingLeoninFilter() { - super("creature that's attacking you"); - } - - public StalkingLeoninFilter(final StalkingLeoninFilter filter) { - super(filter); - } - - @Override - public StalkingLeoninFilter copy() { - return new StalkingLeoninFilter(this); - } - - @Override - public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - return super.match(permanent, sourceId, playerId, game) - && playerId.equals(game.getCombat().getDefenderId(permanent.getId())); - } -} From 1b5c1cdb61dddbd1659a0340ad2ae58288f60700 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 15:19:07 -0400 Subject: [PATCH 36/48] Implemented Righteous War --- Mage.Sets/src/mage/cards/r/RighteousWar.java | 89 ++++++++++++++++++++ Mage.Sets/src/mage/sets/Visions.java | 1 + 2 files changed, 90 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RighteousWar.java diff --git a/Mage.Sets/src/mage/cards/r/RighteousWar.java b/Mage.Sets/src/mage/cards/r/RighteousWar.java new file mode 100644 index 00000000000..b64fd498752 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RighteousWar.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.r; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author TheElk801 + */ +public class RighteousWar extends CardImpl { + + private static final FilterCreaturePermanent whiteFilter = new FilterCreaturePermanent("white creatures you control"); + private static final FilterCreaturePermanent blackFilter = new FilterCreaturePermanent("black creatures you control"); + + static { + whiteFilter.add(new ColorPredicate(ObjectColor.WHITE)); + blackFilter.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public RighteousWar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{B}"); + + // White creatures you control have protection from black. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityControlledEffect( + ProtectionAbility.from(ObjectColor.BLACK), + Duration.WhileOnBattlefield, + whiteFilter + ) + )); + + // Black creatures you control have protection from white. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityControlledEffect( + ProtectionAbility.from(ObjectColor.WHITE), + Duration.WhileOnBattlefield, + blackFilter + ) + )); + } + + public RighteousWar(final RighteousWar card) { + super(card); + } + + @Override + public RighteousWar copy() { + return new RighteousWar(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index 06465ba0d50..60a268a46fa 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -147,6 +147,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Resistance Fighter", 118, Rarity.COMMON, mage.cards.r.ResistanceFighter.class)); cards.add(new SetCardInfo("Retribution of the Meek", 119, Rarity.RARE, mage.cards.r.RetributionOfTheMeek.class)); cards.add(new SetCardInfo("Righteous Aura", 120, Rarity.COMMON, mage.cards.r.RighteousAura.class)); + cards.add(new SetCardInfo("Righteous War", 134, Rarity.RARE, mage.cards.r.RighteousWar.class)); cards.add(new SetCardInfo("River Boa", 68, Rarity.COMMON, mage.cards.r.RiverBoa.class)); cards.add(new SetCardInfo("Rock Slide", 92, Rarity.COMMON, mage.cards.r.RockSlide.class)); cards.add(new SetCardInfo("Rowen", 69, Rarity.RARE, mage.cards.r.Rowen.class)); From 197aa33fc05fc59cf34dfe0fea8132debfbcb6d0 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 18 Oct 2017 21:33:05 +0200 Subject: [PATCH 37/48] Removed Target Source redundancy --- Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java index 23b9527c5f2..971e38c0d85 100644 --- a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java +++ b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java @@ -69,9 +69,7 @@ public class OpalEyeKondasYojimbo extends CardImpl { this.addAbility(new BushidoAbility(1)); // {T}: The next time a source of your choice would deal damage this turn, that damage is dealt to Opal-Eye, Konda's Yojimbo instead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OpalEyeKondasYojimboRedirectionEffect(), new TapSourceCost()); - ability.addTarget(new TargetSource()); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new OpalEyeKondasYojimboRedirectionEffect(), new TapSourceCost())); // {1}{W}: Prevent the next 1 damage that would be dealt to Opal-Eye this turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToSourceEffect(Duration.EndOfTurn, 1), new ManaCostsImpl("{1}{W}"))); From c5399460b9e874d4b736e452f49c32f4557ba8f2 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 18 Oct 2017 22:12:27 +0200 Subject: [PATCH 38/48] Rewrote Choose Blockers effect to ContinuousRuleModifyingEffectImpl --- Mage.Sets/src/mage/cards/m/MasterWarcraft.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index cfbf33e8c15..2b07a65470b 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -34,7 +34,6 @@ import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -238,10 +237,10 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } } -class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { // TODO: replace this with ContinuousRuleModifyingEffectImpl +class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { // TODO: reverse the resolution order in case of effect multiples public MasterWarcraftChooseBlockersEffect() { - super(Duration.EndOfTurn, Outcome.Benefit); + super(Duration.EndOfTurn, Outcome.Benefit, false, false); staticText = "You choose which creatures block this turn and how those creatures block"; } @@ -266,20 +265,11 @@ class MasterWarcraftChooseBlockersEffect extends ReplacementEffectImpl { // TODO @Override public boolean applies(GameEvent event, Ability source, Game game) { - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { - return true; - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player blockController = game.getPlayer(source.getControllerId()); if (blockController != null) { game.getCombat().selectBlockers(blockController, game); return true; } return false; - } + } } From 5d126afc18df13a24d023292d238433f38c90311 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 18 Oct 2017 22:18:32 +0200 Subject: [PATCH 39/48] Rewrote Choose Blockers effect to ContinuousRuleModifyingEffectImpl --- .../src/mage/cards/b/BrutalHordechief.java | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java index 3311459c095..ff4b4e3cfe3 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java +++ b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java @@ -33,7 +33,7 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.combat.BlocksIfAbleAllEffect; @@ -71,7 +71,7 @@ public class BrutalHordechief extends CardImpl { // {3}{R/W}{R/W}: Creatures your opponents control block this turn if able, and you choose how those creatures block. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BlocksIfAbleAllEffect(filter, Duration.EndOfTurn), new ManaCostsImpl("{3}{R/W}{R/W}")); - ability.addEffect(new BrutalHordechiefReplacementEffect()); + ability.addEffect(new BrutalHordechiefChooseBlockersEffect()); this.addAbility(ability); } @@ -123,20 +123,20 @@ class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl { } } -class BrutalHordechiefReplacementEffect extends ReplacementEffectImpl { +class BrutalHordechiefChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { // TODO: reverse the resolution order in case of effect multiples - public BrutalHordechiefReplacementEffect() { - super(Duration.EndOfCombat, Outcome.Benefit); - staticText = ", and you choose how those creatures block"; + public BrutalHordechiefChooseBlockersEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, false, false); + staticText = "You choose which creatures block this turn and how those creatures block"; } - public BrutalHordechiefReplacementEffect(final BrutalHordechiefReplacementEffect effect) { + public BrutalHordechiefChooseBlockersEffect(final BrutalHordechiefChooseBlockersEffect effect) { super(effect); } @Override - public BrutalHordechiefReplacementEffect copy() { - return new BrutalHordechiefReplacementEffect(this); + public BrutalHordechiefChooseBlockersEffect copy() { + return new BrutalHordechiefChooseBlockersEffect(this); } @Override @@ -153,18 +153,10 @@ class BrutalHordechiefReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { Player blockController = game.getPlayer(source.getControllerId()); if (blockController != null) { - return true; - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { + game.informPlayers(source.getSourceObject(game).getIdName() + " applies"); game.getCombat().selectBlockers(blockController, game); return true; } return false; - } + } } From 12d74214ed9e6b89a52f7d040d0a70bbb9ecd896 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 18 Oct 2017 22:20:21 +0200 Subject: [PATCH 40/48] Some stray text fixes --- Mage.Sets/src/mage/cards/b/BrutalHordechief.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java index ff4b4e3cfe3..108280010f7 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java +++ b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java @@ -127,7 +127,7 @@ class BrutalHordechiefChooseBlockersEffect extends ContinuousRuleModifyingEffect public BrutalHordechiefChooseBlockersEffect() { super(Duration.EndOfTurn, Outcome.Benefit, false, false); - staticText = "You choose which creatures block this turn and how those creatures block"; + staticText = ", and you choose how those creatures block"; } public BrutalHordechiefChooseBlockersEffect(final BrutalHordechiefChooseBlockersEffect effect) { @@ -153,7 +153,6 @@ class BrutalHordechiefChooseBlockersEffect extends ContinuousRuleModifyingEffect public boolean applies(GameEvent event, Ability source, Game game) { Player blockController = game.getPlayer(source.getControllerId()); if (blockController != null) { - game.informPlayers(source.getSourceObject(game).getIdName() + " applies"); game.getCombat().selectBlockers(blockController, game); return true; } From bc6104d201a5402a3e9acaf2e1b8c6fe3d5a136d Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 18 Oct 2017 22:36:11 -0400 Subject: [PATCH 41/48] Implemented Suleiman's Legacy --- .../src/mage/cards/s/SuleimansLegacy.java | 81 +++++++++++++++++++ Mage.Sets/src/mage/sets/Visions.java | 1 + 2 files changed, 82 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SuleimansLegacy.java diff --git a/Mage.Sets/src/mage/cards/s/SuleimansLegacy.java b/Mage.Sets/src/mage/cards/s/SuleimansLegacy.java new file mode 100644 index 00000000000..5bb92a92e61 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuleimansLegacy.java @@ -0,0 +1,81 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author TheElk801 + */ +public class SuleimansLegacy extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Djinns and Efreets"); + + static { + filter.add(Predicates.or( + new SubtypePredicate(SubType.DJINN), + new SubtypePredicate(SubType.EFREET) + )); + } + + public SuleimansLegacy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{W}"); + + // When Suleiman's Legacy enters the battlefield, destroy all Djinns and Efreets. They can't be regenerated. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter, true))); + + // Whenever a Djinn or Efreet enters the battlefield, destroy it. It can't be regenerated. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, new DestroyTargetEffect(true), filter, false, SetTargetPointer.PERMANENT, + "Whenever a Djinn or Efreet enters the battlefield, destroy it. It can't be regenerated." + )); + } + + public SuleimansLegacy(final SuleimansLegacy card) { + super(card); + } + + @Override + public SuleimansLegacy copy() { + return new SuleimansLegacy(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index 60a268a46fa..7f7d25ed48b 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -162,6 +162,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Spitting Drake", 95, Rarity.UNCOMMON, mage.cards.s.SpittingDrake.class)); cards.add(new SetCardInfo("Squandered Resources", 137, Rarity.RARE, mage.cards.s.SquanderedResources.class)); cards.add(new SetCardInfo("Stampeding Wildebeests", 71, Rarity.UNCOMMON, mage.cards.s.StampedingWildebeests.class)); + cards.add(new SetCardInfo("Suleiman's Legacy", 138, Rarity.RARE, mage.cards.s.SuleimansLegacy.class)); cards.add(new SetCardInfo("Summer Bloom", 72, Rarity.UNCOMMON, mage.cards.s.SummerBloom.class)); cards.add(new SetCardInfo("Sun Clasp", 121, Rarity.COMMON, mage.cards.s.SunClasp.class)); cards.add(new SetCardInfo("Suq'Ata Assassin", 19, Rarity.UNCOMMON, mage.cards.s.SuqAtaAssassin.class)); From 668a93d681da4ad67138c41642eb3498bf6def5b Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 19 Oct 2017 07:01:39 +0200 Subject: [PATCH 42/48] Improved targetting of MasterWarcraftAttackEffect --- Mage.Sets/src/mage/cards/m/MasterWarcraft.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 2b07a65470b..d16000e64ee 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -49,6 +49,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; import mage.target.targetpointer.FixedTarget; /** @@ -128,6 +129,11 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI } class MasterWarcraftAttackEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"); + static { + filter.add(new ControllerPredicate(TargetController.ACTIVE)); + } MasterWarcraftAttackEffect() { super(Outcome.Benefit); @@ -146,9 +152,8 @@ class MasterWarcraftAttackEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - // TODO: find a way to undo creature selection - Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"), true); - if (target.choose(Outcome.Neutral, source.getControllerId(), source.getSourceId(), game)) { + Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); + if (controller.chooseTarget(Outcome.Benefit, target, source, game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { // Choose creatures that will be attacking this combat @@ -237,7 +242,7 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } } -class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { // TODO: reverse the resolution order in case of effect multiples +class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { // TODO: fix sorting order in case of Master Warcraft multiples public MasterWarcraftChooseBlockersEffect() { super(Duration.EndOfTurn, Outcome.Benefit, false, false); From 1452e101288cc5004962043557f247d041d84e3d Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 19 Oct 2017 07:34:46 -0400 Subject: [PATCH 43/48] Implemented Waylay --- Mage.Sets/src/mage/cards/w/Waylay.java | 103 ++++++++++++++++++ Mage.Sets/src/mage/sets/UrzasSaga.java | 1 + .../game/permanent/token/WaylayToken.java | 48 ++++++++ 3 files changed, 152 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/Waylay.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/WaylayToken.java diff --git a/Mage.Sets/src/mage/cards/w/Waylay.java b/Mage.Sets/src/mage/cards/w/Waylay.java new file mode 100644 index 00000000000..795197e9294 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/Waylay.java @@ -0,0 +1,103 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextCleanupDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Token; +import mage.game.permanent.token.WaylayToken; +import mage.target.targetpointer.FixedTargets; + +/** + * + * @author TheElk801 + */ +public class Waylay extends CardImpl { + + public Waylay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Create three 2/2 white Knight creature tokens. Exile them at the beginning of the next cleanup step. + this.getSpellAbility().addEffect(new WaylayEffect()); + } + + public Waylay(final Waylay card) { + super(card); + } + + @Override + public Waylay copy() { + return new Waylay(this); + } +} + +class WaylayEffect extends OneShotEffect { + + public WaylayEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Create three 2/2 white Knight creature tokens. Exile them at the beginning of the next cleanup step."; + } + + public WaylayEffect(final WaylayEffect effect) { + super(effect); + } + + @Override + public WaylayEffect copy() { + return new WaylayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new WaylayToken(); + token.putOntoBattlefield(3, game, source.getSourceId(), source.getControllerId()); + List toExile = new ArrayList<>(); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent != null) { + toExile.add(tokenPermanent); + } + } + Effect effect = new ExileTargetEffect(); + effect.setTargetPointer(new FixedTargets(toExile, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextCleanupDelayedTriggeredAbility(effect), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasSaga.java b/Mage.Sets/src/mage/sets/UrzasSaga.java index 3968d93c04e..e6c428107ea 100644 --- a/Mage.Sets/src/mage/sets/UrzasSaga.java +++ b/Mage.Sets/src/mage/sets/UrzasSaga.java @@ -362,6 +362,7 @@ public class UrzasSaga extends ExpansionSet { cards.add(new SetCardInfo("Vug Lizard", 227, Rarity.UNCOMMON, mage.cards.v.VugLizard.class)); cards.add(new SetCardInfo("War Dance", 282, Rarity.UNCOMMON, mage.cards.w.WarDance.class)); cards.add(new SetCardInfo("Wall of Junk", 315, Rarity.UNCOMMON, mage.cards.w.WallOfJunk.class)); + cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class)); cards.add(new SetCardInfo("Western Paladin", 168, Rarity.RARE, mage.cards.w.WesternPaladin.class)); cards.add(new SetCardInfo("Whetstone", 316, Rarity.RARE, mage.cards.w.Whetstone.class)); cards.add(new SetCardInfo("Whirlwind", 283, Rarity.RARE, mage.cards.w.Whirlwind.class)); diff --git a/Mage/src/main/java/mage/game/permanent/token/WaylayToken.java b/Mage/src/main/java/mage/game/permanent/token/WaylayToken.java new file mode 100644 index 00000000000..47942f2c6a5 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WaylayToken.java @@ -0,0 +1,48 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com AS IS AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.permanent.token; + +import mage.constants.CardType; +import mage.constants.SubType; +import mage.MageInt; + +/** + * + * @author spjspj + */ +public class WaylayToken extends Token { + + public WaylayToken() { + super("Knight", "2/2 white Knight creature token"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add(SubType.KNIGHT); + power = new MageInt(2); + toughness = new MageInt(2); + } +} From d52f46323b02716fbcaae0f474292bf947b1a716 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 19 Oct 2017 13:55:16 -0400 Subject: [PATCH 44/48] Updated Adam Styborski's Pauper Cube --- .../cubes/AdamStyborskisPauperCube.java | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java index ba99a83ba60..4ef11e8d506 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java @@ -35,9 +35,9 @@ import mage.game.draft.DraftCube; */ public class AdamStyborskisPauperCube extends DraftCube { -public AdamStyborskisPauperCube() { + public AdamStyborskisPauperCube() { super("Adam Styborkski's Pauper Cube"); // https://docs.google.com/spreadsheets/d/12iQhC4bHqFW7hEWxPBjyC8yBDehFZ0_4DkqzyA8EL3o/edit#gid=0 - // last updated with Amonkhet 6/12/17 + // last updated with Hour of Devastation, Iconic Masters and Ixalan 10/18/17 cubeCards.add(new CardIdentity("Act of Treason", "")); cubeCards.add(new CardIdentity("Adventuring Gear", "")); cubeCards.add(new CardIdentity("Aerie Ouphes", "")); @@ -45,6 +45,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Aethersnipe", "")); cubeCards.add(new CardIdentity("Agony Warp", "")); cubeCards.add(new CardIdentity("Aim High", "")); + cubeCards.add(new CardIdentity("Ambuscade", "")); cubeCards.add(new CardIdentity("Ambush Viper", "")); cubeCards.add(new CardIdentity("Angelic Purge", "")); cubeCards.add(new CardIdentity("Arachnus Web", "")); @@ -55,18 +56,18 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Ashes to Ashes", "")); cubeCards.add(new CardIdentity("Assault Zeppelid", "")); cubeCards.add(new CardIdentity("Attended Knight", "")); - cubeCards.add(new CardIdentity("Augur of Bolas", "")); cubeCards.add(new CardIdentity("Auger Spree", "")); + cubeCards.add(new CardIdentity("Augur of Bolas", "")); cubeCards.add(new CardIdentity("Aven Riftwatcher", "")); cubeCards.add(new CardIdentity("Aven Surveyor", "")); cubeCards.add(new CardIdentity("Azorius Guildgate", "")); cubeCards.add(new CardIdentity("Baleful Eidolon", "")); cubeCards.add(new CardIdentity("Barbed Lightning", "")); - cubeCards.add(new CardIdentity("Barren Moor", "")); cubeCards.add(new CardIdentity("Basking Rootwalla", "")); cubeCards.add(new CardIdentity("Battle Screech", "")); cubeCards.add(new CardIdentity("Beetleback Chief", "")); cubeCards.add(new CardIdentity("Beetleform Mage", "")); + cubeCards.add(new CardIdentity("Beneath the Sands", "")); cubeCards.add(new CardIdentity("Blastoderm", "")); cubeCards.add(new CardIdentity("Blazing Torch", "")); cubeCards.add(new CardIdentity("Blightning", "")); @@ -78,8 +79,9 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Bonesplitter", "")); cubeCards.add(new CardIdentity("Borderland Marauder", "")); cubeCards.add(new CardIdentity("Boros Guildgate", "")); - cubeCards.add(new CardIdentity("Borrowed Grave", "")); + cubeCards.add(new CardIdentity("Borrowed Grace", "")); cubeCards.add(new CardIdentity("Branching Bolt", "")); + cubeCards.add(new CardIdentity("Brazen Buccaneers", "")); cubeCards.add(new CardIdentity("Brazen Wolves", "")); cubeCards.add(new CardIdentity("Burning-Tree Emissary", "")); cubeCards.add(new CardIdentity("Burst Lightning", "")); @@ -97,6 +99,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Chain Lightning", "")); cubeCards.add(new CardIdentity("Chainer's Edict", "")); cubeCards.add(new CardIdentity("Chatter of the Squirrel", "")); + cubeCards.add(new CardIdentity("Cinder Barrens", "")); cubeCards.add(new CardIdentity("Citanul Woodreaders", "")); cubeCards.add(new CardIdentity("Claustrophobia", "")); cubeCards.add(new CardIdentity("Clay Statue", "")); @@ -108,15 +111,12 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Compulsive Research", "")); cubeCards.add(new CardIdentity("Compulsory Rest", "")); cubeCards.add(new CardIdentity("Consume Strength", "")); - cubeCards.add(new CardIdentity("Corpse Churn", "")); cubeCards.add(new CardIdentity("Corrupted Zendikon", "")); cubeCards.add(new CardIdentity("Counterspell", "")); cubeCards.add(new CardIdentity("Crippling Fatigue", "")); cubeCards.add(new CardIdentity("Crow of Dark Tidings", "")); cubeCards.add(new CardIdentity("Crypt Rats", "")); cubeCards.add(new CardIdentity("Crystallization", "")); - cubeCards.add(new CardIdentity("Cultist's Staff", "")); - cubeCards.add(new CardIdentity("Cultivate", "")); cubeCards.add(new CardIdentity("Cunning Strike", "")); cubeCards.add(new CardIdentity("Curse of Chains", "")); cubeCards.add(new CardIdentity("Custodi Squire", "")); @@ -124,9 +124,11 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Dauntless Cathar", "")); cubeCards.add(new CardIdentity("Dead Reveler", "")); cubeCards.add(new CardIdentity("Dead Weight", "")); + cubeCards.add(new CardIdentity("Deadeye Tormentor", "")); cubeCards.add(new CardIdentity("Death Denied", "")); cubeCards.add(new CardIdentity("Deep Analysis", "")); cubeCards.add(new CardIdentity("Deprive", "")); + cubeCards.add(new CardIdentity("Depths of Desire", "")); cubeCards.add(new CardIdentity("Deputy of Acquittals", "")); cubeCards.add(new CardIdentity("Desert", "")); cubeCards.add(new CardIdentity("Desperate Sentry", "")); @@ -134,6 +136,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Diabolic Edict", "")); cubeCards.add(new CardIdentity("Dimir Guildgate", "")); cubeCards.add(new CardIdentity("Dinrova Horror", "")); + cubeCards.add(new CardIdentity("Dire Fleet Hoarder", "")); cubeCards.add(new CardIdentity("Disfigure", "")); cubeCards.add(new CardIdentity("Dismal Backwater", "")); cubeCards.add(new CardIdentity("Disowned Ancestor", "")); @@ -152,20 +155,21 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Epic Confrontation", "")); cubeCards.add(new CardIdentity("Errant Ephemeron", "")); cubeCards.add(new CardIdentity("Esper Cormorants", "")); - cubeCards.add(new CardIdentity("Essence Scatter", "")); cubeCards.add(new CardIdentity("Evincar's Justice", "")); cubeCards.add(new CardIdentity("Evolving Wilds", "")); cubeCards.add(new CardIdentity("Faceless Butcher", "")); cubeCards.add(new CardIdentity("Faith's Fetters", "")); + cubeCards.add(new CardIdentity("Falkenrath Noble", "")); cubeCards.add(new CardIdentity("Feeling of Dread", "")); - cubeCards.add(new CardIdentity("Fertile Thicket", "")); cubeCards.add(new CardIdentity("Fervent Cathar", "")); cubeCards.add(new CardIdentity("Firebolt", "")); cubeCards.add(new CardIdentity("Fireslinger", "")); cubeCards.add(new CardIdentity("Flayer Husk", "")); cubeCards.add(new CardIdentity("Flurry of Horns", "")); cubeCards.add(new CardIdentity("Forked Bolt", "")); + cubeCards.add(new CardIdentity("Forsaken Sanctuary", "")); cubeCards.add(new CardIdentity("Fortify", "")); + cubeCards.add(new CardIdentity("Foul Orchard", "")); cubeCards.add(new CardIdentity("Frilled Oculus", "")); cubeCards.add(new CardIdentity("Frostburn Weird", "")); cubeCards.add(new CardIdentity("Gathan Raiders", "")); @@ -182,21 +186,22 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Gods Willing", "")); cubeCards.add(new CardIdentity("Goldmeadow Harrier", "")); cubeCards.add(new CardIdentity("Golgari Guildgate", "")); - cubeCards.add(new CardIdentity("Grapple with the Past", "")); cubeCards.add(new CardIdentity("Gravedigger", "")); cubeCards.add(new CardIdentity("Gray Merchant of Asphodel", "")); + cubeCards.add(new CardIdentity("Grazing Whiptail", "")); cubeCards.add(new CardIdentity("Grim Contest", "")); cubeCards.add(new CardIdentity("Grisly Salvage", "")); cubeCards.add(new CardIdentity("Grixis Slavedriver", "")); cubeCards.add(new CardIdentity("Gruul Guildgate", "")); cubeCards.add(new CardIdentity("Gryff Vanguard", "")); cubeCards.add(new CardIdentity("Guardian Automaton", "")); + cubeCards.add(new CardIdentity("Guardian Idol", "")); cubeCards.add(new CardIdentity("Guardian of the Guildpact", "")); cubeCards.add(new CardIdentity("Gurmag Angler", "")); - cubeCards.add(new CardIdentity("Halimar Depths", "")); cubeCards.add(new CardIdentity("Halimar Wavewatch", "")); cubeCards.add(new CardIdentity("Harrow", "")); cubeCards.add(new CardIdentity("Harsh Sustenance", "")); + cubeCards.add(new CardIdentity("Highland Lake", "")); cubeCards.add(new CardIdentity("Hissing Iguanar", "")); cubeCards.add(new CardIdentity("Hooting Mandrills", "")); cubeCards.add(new CardIdentity("Humble", "")); @@ -206,6 +211,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Insolent Neonate", "")); cubeCards.add(new CardIdentity("Into the Roil", "")); cubeCards.add(new CardIdentity("Isolation Zone", "")); + cubeCards.add(new CardIdentity("Ivy Elemental", "")); cubeCards.add(new CardIdentity("Izzet Chronarch", "")); cubeCards.add(new CardIdentity("Izzet Guildgate", "")); cubeCards.add(new CardIdentity("Jilt", "")); @@ -214,7 +220,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Kabuto Moth", "")); cubeCards.add(new CardIdentity("Keldon Marauders", "")); cubeCards.add(new CardIdentity("Kingpin's Pet", "")); - cubeCards.add(new CardIdentity("Kodama's Reach", "")); cubeCards.add(new CardIdentity("Kor Skyfisher", "")); cubeCards.add(new CardIdentity("Kozilek's Channeler", "")); cubeCards.add(new CardIdentity("Krenko's Command", "")); @@ -222,25 +227,24 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Kruin Striker", "")); cubeCards.add(new CardIdentity("Lash Out", "")); cubeCards.add(new CardIdentity("Last Gasp", "")); - cubeCards.add(new CardIdentity("Lawless Broker", "")); + cubeCards.add(new CardIdentity("Leave in the Dust", "")); cubeCards.add(new CardIdentity("Leonin Bola", "")); - cubeCards.add(new CardIdentity("Leonin Scimitar", "")); + cubeCards.add(new CardIdentity("Lifecraft Cavalry", "")); cubeCards.add(new CardIdentity("Lightning Bolt", "")); cubeCards.add(new CardIdentity("Liliana's Specter", "")); - cubeCards.add(new CardIdentity("Looming Spires", "")); cubeCards.add(new CardIdentity("Lotus Path Djinn", "")); cubeCards.add(new CardIdentity("Loyal Pegasus", "")); cubeCards.add(new CardIdentity("Lurking Automaton", "")); cubeCards.add(new CardIdentity("Magma Jet", "")); cubeCards.add(new CardIdentity("Makeshift Mauler", "")); + cubeCards.add(new CardIdentity("Man-o'-War", "")); cubeCards.add(new CardIdentity("Mana Leak", "")); cubeCards.add(new CardIdentity("Manticore of the Gauntlet", "")); - cubeCards.add(new CardIdentity("Man-o'-War", "")); cubeCards.add(new CardIdentity("Mardu Hordechief", "")); cubeCards.add(new CardIdentity("Mardu Skullhunter", "")); cubeCards.add(new CardIdentity("Maul Splicer", "")); - cubeCards.add(new CardIdentity("Maulfist Squad", "")); cubeCards.add(new CardIdentity("Maze of Ith", "")); + cubeCards.add(new CardIdentity("Meandering River", "")); cubeCards.add(new CardIdentity("Mental Note", "")); cubeCards.add(new CardIdentity("Midnight Scavengers", "")); cubeCards.add(new CardIdentity("Mind Stone", "")); @@ -249,6 +253,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Mishra's Factory", "")); cubeCards.add(new CardIdentity("Mist Raven", "")); cubeCards.add(new CardIdentity("Mistral Charger", "")); + cubeCards.add(new CardIdentity("Mobile Garrison", "")); cubeCards.add(new CardIdentity("Mogg Fanatic", "")); cubeCards.add(new CardIdentity("Mogg Flunkies", "")); cubeCards.add(new CardIdentity("Mogg War Marshal", "")); @@ -260,17 +265,17 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Narcolepsy", "")); cubeCards.add(new CardIdentity("Necromancer's Assistant", "")); cubeCards.add(new CardIdentity("Nessian Asp", "")); + cubeCards.add(new CardIdentity("Nest Robber", "")); cubeCards.add(new CardIdentity("Nezumi Cutthroat", "")); cubeCards.add(new CardIdentity("Night's Whisper", "")); cubeCards.add(new CardIdentity("Nightscape Familiar", "")); cubeCards.add(new CardIdentity("Ninja of the Deep Hours", "")); cubeCards.add(new CardIdentity("Oblivion Ring", "")); cubeCards.add(new CardIdentity("Oona's Grace", "")); - cubeCards.add(new CardIdentity("Orcish Oriflamme", "")); cubeCards.add(new CardIdentity("Orzhov Guildgate", "")); cubeCards.add(new CardIdentity("Otherworldly Journey", "")); cubeCards.add(new CardIdentity("Pacifism", "")); - cubeCards.add(new CardIdentity("Palace Sentinels", "")); + cubeCards.add(new CardIdentity("Paladin of the Bloodstained", "")); cubeCards.add(new CardIdentity("Peema Outrider", "")); cubeCards.add(new CardIdentity("Penumbra Spider", "")); cubeCards.add(new CardIdentity("Peregrine Drake", "")); @@ -287,7 +292,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Plated Geopede", "")); cubeCards.add(new CardIdentity("Plover Knights", "")); cubeCards.add(new CardIdentity("Porcelain Legionnaire", "")); - cubeCards.add(new CardIdentity("Pouncing Kavu", "")); + cubeCards.add(new CardIdentity("Pounce", "")); cubeCards.add(new CardIdentity("Predatory Nightstalker", "")); cubeCards.add(new CardIdentity("Preordain", "")); cubeCards.add(new CardIdentity("Prey Upon", "")); @@ -316,27 +321,27 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Renegade Freighter", "")); cubeCards.add(new CardIdentity("Rift Bolt", "")); cubeCards.add(new CardIdentity("Rishadan Airship", "")); - cubeCards.add(new CardIdentity("River Serpent", "")); cubeCards.add(new CardIdentity("Ronin Houndmaster", "")); cubeCards.add(new CardIdentity("Rugged Highlands", "")); cubeCards.add(new CardIdentity("Runed Servitor", "")); cubeCards.add(new CardIdentity("Rushing River", "")); + cubeCards.add(new CardIdentity("Sailor of Means", "")); cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); cubeCards.add(new CardIdentity("Sandsteppe Outcast", "")); cubeCards.add(new CardIdentity("Sangrite Backlash", "")); cubeCards.add(new CardIdentity("Sarkhan's Rage", "")); cubeCards.add(new CardIdentity("Savage Punch", "")); - cubeCards.add(new CardIdentity("Savage Surge", "")); cubeCards.add(new CardIdentity("Scatter the Seeds", "")); - cubeCards.add(new CardIdentity("Scion of the Wild", "")); cubeCards.add(new CardIdentity("Scion Summoner", "")); + cubeCards.add(new CardIdentity("Scion of the Wild", "")); cubeCards.add(new CardIdentity("Scoured Barrens", "")); cubeCards.add(new CardIdentity("Scourge Devil", "")); cubeCards.add(new CardIdentity("Screeching Skaab", "")); cubeCards.add(new CardIdentity("Scuzzback Marauders", "")); + cubeCards.add(new CardIdentity("Search for Tomorrow", "")); cubeCards.add(new CardIdentity("Searing Blaze", "")); cubeCards.add(new CardIdentity("Seeker of Insight", "")); - cubeCards.add(new CardIdentity("Sejiri Steppe", "")); + cubeCards.add(new CardIdentity("Seeker of the Way", "")); cubeCards.add(new CardIdentity("Selesnya Guildgate", "")); cubeCards.add(new CardIdentity("Sentinel Spider", "")); cubeCards.add(new CardIdentity("Separatist Voidmage", "")); @@ -349,7 +354,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Sigil Blessing", "")); cubeCards.add(new CardIdentity("Silent Departure", "")); cubeCards.add(new CardIdentity("Simic Guildgate", "")); - cubeCards.add(new CardIdentity("Skinthinner", "")); cubeCards.add(new CardIdentity("Skirk Marauder", "")); cubeCards.add(new CardIdentity("Skyknight Legionnaire", "")); cubeCards.add(new CardIdentity("Slash Panther", "")); @@ -362,21 +366,21 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Spined Thopter", "")); cubeCards.add(new CardIdentity("Splatter Thug", "")); cubeCards.add(new CardIdentity("Staggershock", "")); - cubeCards.add(new CardIdentity("Stave Off", "")); + cubeCards.add(new CardIdentity("Star Compass", "")); cubeCards.add(new CardIdentity("Stitched Drake", "")); + cubeCards.add(new CardIdentity("Stone Quarry", "")); + cubeCards.add(new CardIdentity("Storm Fleet Pyromancer", "")); cubeCards.add(new CardIdentity("Stormfront Pegasus", "")); cubeCards.add(new CardIdentity("Stormscape Apprentice", "")); cubeCards.add(new CardIdentity("Strip Mine", "")); + cubeCards.add(new CardIdentity("Striped Riverwinder", "")); + cubeCards.add(new CardIdentity("Submerged Boneyard", "")); cubeCards.add(new CardIdentity("Sultai Scavenger", "")); cubeCards.add(new CardIdentity("Suppression Bonds", "")); - cubeCards.add(new CardIdentity("Swift Spinner", "")); cubeCards.add(new CardIdentity("Swiftwater Cliffs", "")); cubeCards.add(new CardIdentity("Sylvan Might", "")); cubeCards.add(new CardIdentity("Sylvok Lifestaff", "")); cubeCards.add(new CardIdentity("Tah-Crop Elite", "")); - cubeCards.add(new CardIdentity("Tajuru Pathwarden", "")); - cubeCards.add(new CardIdentity("Talruum Minotaur", "")); - cubeCards.add(new CardIdentity("Tandem Lookout", "")); cubeCards.add(new CardIdentity("Temporal Isolation", "")); cubeCards.add(new CardIdentity("Tenement Crasher", "")); cubeCards.add(new CardIdentity("Terminate", "")); @@ -387,25 +391,26 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Thornwood Falls", "")); cubeCards.add(new CardIdentity("Thought Scour", "")); cubeCards.add(new CardIdentity("Thraben Inspector", "")); + cubeCards.add(new CardIdentity("Thrill-Kill Assassin", "")); cubeCards.add(new CardIdentity("Thundering Giant", "")); cubeCards.add(new CardIdentity("Thundering Tanadon", "")); cubeCards.add(new CardIdentity("Thunderous Wrath", "")); + cubeCards.add(new CardIdentity("Timber Gorge", "")); cubeCards.add(new CardIdentity("Time to Feed", "")); cubeCards.add(new CardIdentity("Tithe Drinker", "")); cubeCards.add(new CardIdentity("Totem-Guide Hartebeest", "")); cubeCards.add(new CardIdentity("Tragic Slip", "")); cubeCards.add(new CardIdentity("Tranquil Cove", "")); + cubeCards.add(new CardIdentity("Tranquil Expanse", "")); cubeCards.add(new CardIdentity("Travel Preparations", "")); cubeCards.add(new CardIdentity("Treasure Cruise", "")); cubeCards.add(new CardIdentity("Triplicate Spirits", "")); - cubeCards.add(new CardIdentity("Tumble Magnet", "")); cubeCards.add(new CardIdentity("Typhoid Rats", "")); cubeCards.add(new CardIdentity("Ulamog's Crusher", "")); cubeCards.add(new CardIdentity("Ulvenwald Captive", "")); cubeCards.add(new CardIdentity("Undying Rage", "")); cubeCards.add(new CardIdentity("Unearth", "")); cubeCards.add(new CardIdentity("Unmake", "")); - cubeCards.add(new CardIdentity("Unnatural Aggression", "")); cubeCards.add(new CardIdentity("Vampire Interloper", "")); cubeCards.add(new CardIdentity("Vault Skirge", "")); cubeCards.add(new CardIdentity("Viashino Firstblade", "")); @@ -424,7 +429,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Wasteland Scorpion", "")); cubeCards.add(new CardIdentity("Wayfarer's Bauble", "")); cubeCards.add(new CardIdentity("Werebear", "")); - cubeCards.add(new CardIdentity("Whirlpool Whelm", "")); cubeCards.add(new CardIdentity("Whitemane Lion", "")); cubeCards.add(new CardIdentity("Wild Instincts", "")); cubeCards.add(new CardIdentity("Wild Mongrel", "")); @@ -435,6 +439,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Winds of Rebuke", "")); cubeCards.add(new CardIdentity("Winged Coatl", "")); cubeCards.add(new CardIdentity("Wojek Halberdiers", "")); + cubeCards.add(new CardIdentity("Woodland Stream", "")); cubeCards.add(new CardIdentity("Wrecking Ball", "")); cubeCards.add(new CardIdentity("Wretched Gryff", "")); cubeCards.add(new CardIdentity("Yavimaya Elder", "")); From 3558899033cf5c97f773b6f1b8fdc08fc7572742 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 20 Oct 2017 00:41:32 +0200 Subject: [PATCH 45/48] Major code revamp, created a new watcher --- .../src/mage/cards/m/MasterWarcraft.java | 175 ++++++++++-------- 1 file changed, 102 insertions(+), 73 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index d16000e64ee..23c5a7a503a 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -28,6 +28,7 @@ package mage.cards.m; import java.util.*; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; @@ -45,12 +46,15 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.filter.predicate.permanent.ControllerPredicate; import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; /** * @@ -62,13 +66,18 @@ public class MasterWarcraft extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R/W}{R/W}"); // Cast Master Warcraft only before attackers are declared. - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance)); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance, "Cast Master Warcraft only before attackers are declared")); // You choose which creatures attack this turn. this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect()); // You choose which creatures block this turn and how those creatures block. this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect()); + + + // (only the last resolved Master Warcraft spell's effects apply) + this.getSpellAbility().addWatcher(new MasterWarcraftCastWatcher()); + this.getSpellAbility().addEffect(new MasterWarcraftCastWatcherIncrementEffect()); } public MasterWarcraft(final MasterWarcraft card) { @@ -83,6 +92,11 @@ public class MasterWarcraft extends CardImpl { class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"); + static { + filter.add(new ControllerPredicate(TargetController.ACTIVE)); + } + public MasterWarcraftChooseAttackersEffect() { super(Duration.EndOfTurn, Outcome.Benefit, false, false); staticText = "You choose which creatures attack this turn"; @@ -109,61 +123,27 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI @Override public boolean applies(GameEvent event, Ability source, Game game) { - Player chooser = game.getPlayer(source.getControllerId()); - Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId()); - if (chooser != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - // Clears previous instances of "should attack" effects - // ("shouldn't attack" effects don't need cleaning because MasterWarcraftMustAttackRequirementEffect overrides them) - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) { - RequirementEffect effect = entry.getKey(); - if (effect instanceof MasterWarcraftMustAttackRequirementEffect) { - effect.discard(); - } - } - } - new MasterWarcraftAttackEffect().apply(game, source); // Master Warcraft imposes its effect right before the attackers being declared... + MasterWarcraftCastWatcher watcher = (MasterWarcraftCastWatcher) game.getState().getWatchers().get(MasterWarcraftCastWatcher.class.getSimpleName()); + watcher.decrement(); + if (watcher.copyCountApply > 0) { + game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); + return false; } - return false; // ...and then resumes the attack declaration for the active player as normal - } -} - -class MasterWarcraftAttackEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"); - static { - filter.add(new ControllerPredicate(TargetController.ACTIVE)); - } - - MasterWarcraftAttackEffect() { - super(Outcome.Benefit); - } - - MasterWarcraftAttackEffect(final MasterWarcraftAttackEffect effect) { - super(effect); - } - - @Override - public MasterWarcraftAttackEffect copy() { - return new MasterWarcraftAttackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { + watcher.copyCountApply = watcher.copyCount; Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId()); + if (controller != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) { Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); if (controller.chooseTarget(Outcome.Benefit, target, source, game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { // Choose creatures that will be attacking this combat if (target.getTargets().contains(permanent.getId())) { - RequirementEffect effect = new MasterWarcraftMustAttackRequirementEffect(); + RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); effect.setText(""); effect.setTargetPointer(new FixedTarget(permanent.getId())); game.addEffect(effect, source); - // TODO: find a better way to report attackers to game log - // game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " should attack this combat if able"); + game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able"); // All other creatures can't attack } else { @@ -172,36 +152,10 @@ class MasterWarcraftAttackEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(permanent.getId())); game.addEffect(effect, source); } - } - return true; } } - return false; - } -} - -class MasterWarcraftMustAttackRequirementEffect extends AttacksIfAbleTargetEffect { - - MasterWarcraftMustAttackRequirementEffect() { - super(Duration.EndOfCombat); - } - - MasterWarcraftMustAttackRequirementEffect(final MasterWarcraftMustAttackRequirementEffect effect) { - super(effect); - } - - @Override - public MasterWarcraftMustAttackRequirementEffect copy() { - return new MasterWarcraftMustAttackRequirementEffect(this); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - if (discarded) { - return false; - } - return this.getTargetPointer().getTargets(game, source).contains(permanent.getId()); + return false; // the attack declaration resumes for the active player as normal } } @@ -242,7 +196,7 @@ class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { } } -class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { // TODO: fix sorting order in case of Master Warcraft multiples +class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { public MasterWarcraftChooseBlockersEffect() { super(Duration.EndOfTurn, Outcome.Benefit, false, false); @@ -270,6 +224,13 @@ class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectIm @Override public boolean applies(GameEvent event, Ability source, Game game) { + MasterWarcraftCastWatcher watcher = (MasterWarcraftCastWatcher) game.getState().getWatchers().get(MasterWarcraftCastWatcher.class.getSimpleName()); + watcher.decrement(); + if (watcher.copyCountApply > 0) { + game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); + return false; + } + watcher.copyCountApply = watcher.copyCount; Player blockController = game.getPlayer(source.getControllerId()); if (blockController != null) { game.getCombat().selectBlockers(blockController, game); @@ -278,3 +239,71 @@ class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectIm return false; } } + +class MasterWarcraftCastWatcher extends Watcher { + + public int copyCount = 0; + public int copyCountApply = 0; + + public MasterWarcraftCastWatcher() { + super(MasterWarcraftCastWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public MasterWarcraftCastWatcher(final MasterWarcraftCastWatcher watcher) { + super(watcher); + this.copyCount = watcher.copyCount; + this.copyCountApply = watcher.copyCountApply; + } + + @Override + public void reset() { + copyCount = 0; + copyCountApply = 0; + } + + @Override + public MasterWarcraftCastWatcher copy() { + return new MasterWarcraftCastWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + } + + public void increment() { + copyCount++; + copyCountApply = copyCount; + } + + public void decrement() { + if (copyCountApply > 0); { + copyCountApply--; + } + } +} + +class MasterWarcraftCastWatcherIncrementEffect extends OneShotEffect { + + MasterWarcraftCastWatcherIncrementEffect() { + super(Outcome.Neutral); + } + + MasterWarcraftCastWatcherIncrementEffect(final MasterWarcraftCastWatcherIncrementEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + MasterWarcraftCastWatcher watcher = (MasterWarcraftCastWatcher) game.getState().getWatchers().get(MasterWarcraftCastWatcher.class.getSimpleName()); + if (watcher != null) { + watcher.increment(); + return true; + } + return false; + } + + @Override + public MasterWarcraftCastWatcherIncrementEffect copy() { + return new MasterWarcraftCastWatcherIncrementEffect(this); + } +} From 483e6549f6bf8b6acd3bee1cc9f77a12405372bb Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 20 Oct 2017 02:01:09 +0200 Subject: [PATCH 46/48] Cleaned up code, fixed NullPointerException --- .../src/mage/cards/m/MasterWarcraft.java | 57 +++++-------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 23c5a7a503a..7bac5394538 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -39,6 +39,7 @@ import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; +import mage.abilities.effects.common.combat.CantAttackTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -145,12 +146,21 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI game.addEffect(effect, source); game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able"); - // All other creatures can't attack + // All other creatures can't attack (unless they must attack) } else { - RestrictionEffect effect = new MasterWarcraftCantAttackRestrictionEffect(); - effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(effect, source); + boolean hasToAttack = false; + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) { + RequirementEffect effect2 = entry.getKey(); + if (effect2.mustAttack(game)) { + hasToAttack = true; + } + } + if (!hasToAttack) { + RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat); + effect.setText(""); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } } } } @@ -159,43 +169,6 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI } } -class MasterWarcraftCantAttackRestrictionEffect extends RestrictionEffect { - - MasterWarcraftCantAttackRestrictionEffect() { - super(Duration.EndOfCombat); - } - - MasterWarcraftCantAttackRestrictionEffect(final MasterWarcraftCantAttackRestrictionEffect effect) { - super(effect); - } - - @Override - public MasterWarcraftCantAttackRestrictionEffect copy() { - return new MasterWarcraftCantAttackRestrictionEffect(this); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return this.getTargetPointer().getFirst(game, source).equals(permanent.getId()); - } - - @Override - public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, false, game).entrySet()) { - RequirementEffect effect = entry.getKey(); - if (effect.mustAttack(game)) { - return true; - } - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Unless {this} must attack, {this} can't attack."; - } -} - class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { public MasterWarcraftChooseBlockersEffect() { From b30335e3440ab26e4ffb725cb05b486a68efca73 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 20 Oct 2017 10:41:33 -0400 Subject: [PATCH 47/48] Implemented Momentum --- Mage.Sets/src/mage/cards/m/Momentum.java | 83 +++++++++++++++++++ Mage.Sets/src/mage/sets/UrzasDestiny.java | 1 + .../main/java/mage/counters/CounterType.java | 1 + 3 files changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/Momentum.java diff --git a/Mage.Sets/src/mage/cards/m/Momentum.java b/Mage.Sets/src/mage/cards/m/Momentum.java new file mode 100644 index 00000000000..4d54ffdf882 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/Momentum.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; + +/** + * + * @author TheElk801 + */ +public class Momentum extends CardImpl { + + public Momentum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put a growth counter on Momentum. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.GROWTH.createInstance(), true), TargetController.YOU, true)); + + // Enchanted creature gets +1/+1 for each growth counter on Momentum. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(new CountersSourceCount(CounterType.GROWTH), new CountersSourceCount(CounterType.GROWTH)))); + } + + public Momentum(final Momentum card) { + super(card); + } + + @Override + public Momentum copy() { + return new Momentum(this); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index 6a243198e5f..acec22dff22 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -120,6 +120,7 @@ public class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Mental Discipline", 37, Rarity.COMMON, mage.cards.m.MentalDiscipline.class)); cards.add(new SetCardInfo("Metalworker", 135, Rarity.RARE, mage.cards.m.Metalworker.class)); cards.add(new SetCardInfo("Metathran Soldier", 39, Rarity.COMMON, mage.cards.m.MetathranSoldier.class)); + cards.add(new SetCardInfo("Momentum", 113, Rarity.UNCOMMON, mage.cards.m.Momentum.class)); cards.add(new SetCardInfo("Multani's Decree", 114, Rarity.COMMON, mage.cards.m.MultanisDecree.class)); cards.add(new SetCardInfo("Opalescence", 13, Rarity.RARE, mage.cards.o.Opalescence.class)); cards.add(new SetCardInfo("Opposition", 40, Rarity.RARE, mage.cards.o.Opposition.class)); diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index d4b4464157d..1b7862d19e1 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -68,6 +68,7 @@ public enum CounterType { FURY("fury"), FUSE("fuse"), GOLD("gold"), + GROWTH("growth"), HATCHLING("hatchling"), HEALING("healing"), HOOFPRINT("hoofprint"), From 147bc413a78235cf6d464eef4a6dcc3e6ceb100f Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 20 Oct 2017 10:57:41 -0400 Subject: [PATCH 48/48] Implemented Tribal Golem --- Mage.Sets/src/mage/cards/t/TribalGolem.java | 126 ++++++++++++++++++++ Mage.Sets/src/mage/sets/Onslaught.java | 1 + 2 files changed, 127 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TribalGolem.java diff --git a/Mage.Sets/src/mage/cards/t/TribalGolem.java b/Mage.Sets/src/mage/cards/t/TribalGolem.java new file mode 100644 index 00000000000..f29eb8611b3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TribalGolem.java @@ -0,0 +1,126 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author TheElk801 + */ +public class TribalGolem extends CardImpl { + + private static final FilterControlledPermanent filter1 = new FilterControlledPermanent("a Beast"); + private static final FilterControlledPermanent filter2 = new FilterControlledPermanent("a Goblin"); + private static final FilterControlledPermanent filter3 = new FilterControlledPermanent("a Soldier"); + private static final FilterControlledPermanent filter4 = new FilterControlledPermanent("a Wizard"); + private static final FilterControlledPermanent filter5 = new FilterControlledPermanent("a Zombie"); + + static { + filter1.add(new SubtypePredicate(SubType.BEAST)); + filter2.add(new SubtypePredicate(SubType.GOBLIN)); + filter3.add(new SubtypePredicate(SubType.SOLDIER)); + filter4.add(new SubtypePredicate(SubType.WIZARD)); + filter5.add(new SubtypePredicate(SubType.ZOMBIE)); + } + + public TribalGolem(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Tribal Golem has trample as long as you control a Beast, haste as long as you control a Goblin, first strike as long as you control a Soldier, flying as long as you control a Wizard, and "{B}: Regenerate Tribal Golem" as long as you control a Zombie. + Effect effect1 = new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance()), + new PermanentsOnTheBattlefieldCondition(filter1), + "{this} has trample as long as you control a Beast," + ); + Effect effect2 = new ConditionalContinuousEffect( + new GainAbilitySourceEffect(HasteAbility.getInstance()), + new PermanentsOnTheBattlefieldCondition(filter2), + "haste as long as you control a Goblin," + ); + Effect effect3 = new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), + new PermanentsOnTheBattlefieldCondition(filter3), + "first strike as long as you control a Soldier," + ); + Effect effect4 = new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance()), + new PermanentsOnTheBattlefieldCondition(filter4), + "flying as long as you control a Wizard," + ); + Effect effect5 = new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new RegenerateSourceEffect(), + new ManaCostsImpl("{B}") + )), + new PermanentsOnTheBattlefieldCondition(filter5), + "and \"{B}: Regenerate {this}\" as long as you control a Zombie" + ); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1); + ability.addEffect(effect2); + ability.addEffect(effect3); + ability.addEffect(effect4); + ability.addEffect(effect5); + this.addAbility(ability); + } + + public TribalGolem(final TribalGolem card) { + super(card); + } + + @Override + public TribalGolem copy() { + return new TribalGolem(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Onslaught.java b/Mage.Sets/src/mage/sets/Onslaught.java index ae21f921fcc..f8311aadc5f 100644 --- a/Mage.Sets/src/mage/sets/Onslaught.java +++ b/Mage.Sets/src/mage/sets/Onslaught.java @@ -311,6 +311,7 @@ public class Onslaught extends ExpansionSet { cards.add(new SetCardInfo("Trade Secrets", 118, Rarity.RARE, mage.cards.t.TradeSecrets.class)); cards.add(new SetCardInfo("Tranquil Thicket", 326, Rarity.COMMON, mage.cards.t.TranquilThicket.class)); cards.add(new SetCardInfo("Treespring Lorian", 293, Rarity.COMMON, mage.cards.t.TreespringLorian.class)); + cards.add(new SetCardInfo("Tribal Golem", 311, Rarity.RARE, mage.cards.t.TribalGolem.class)); cards.add(new SetCardInfo("Tribal Unity", 294, Rarity.UNCOMMON, mage.cards.t.TribalUnity.class)); cards.add(new SetCardInfo("Trickery Charm", 119, Rarity.COMMON, mage.cards.t.TrickeryCharm.class)); cards.add(new SetCardInfo("True Believer", 57, Rarity.RARE, mage.cards.t.TrueBeliever.class));