From 039c4f22a6f16cf06a4e327ddf96488cdd40cb89 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 14:53:58 +0200 Subject: [PATCH 01/12] * Wither - Added reminder text. --- Mage/src/mage/abilities/keyword/WitherAbility.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/abilities/keyword/WitherAbility.java b/Mage/src/mage/abilities/keyword/WitherAbility.java index 238a7511e80..2dc2c5d8c84 100644 --- a/Mage/src/mage/abilities/keyword/WitherAbility.java +++ b/Mage/src/mage/abilities/keyword/WitherAbility.java @@ -65,7 +65,7 @@ public class WitherAbility extends StaticAbility implements MageSingleton { @Override public String getRule() { - return "Wither"; + return "Wither (This deals damage to creatures in the form of -1/-1 counters.)"; } @Override From ce64a22c155ec984f23f56d00136ee7f47126295 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 15:35:40 +0200 Subject: [PATCH 02/12] Some more rework of player.moveCard (mainly to graveyard). --- .../sets/alarareborn/EtherwroughtPage.java | 12 ++-- .../mage/sets/alarareborn/MindFuneral.java | 4 +- .../mage/sets/alliances/HelmOfObedience.java | 38 ++++++----- .../sets/bornofthegods/SatyrWayfinder.java | 22 +++---- .../championsofkamigawa/InameDeathAspect.java | 9 +-- .../src/mage/sets/commander/BuriedAlive.java | 20 ++---- .../sets/commander/SzadekLordOfSecrets.java | 23 ++++--- .../sets/commander2013/StrategicPlanning.java | 30 +++------ .../mage/sets/conflux/TeleminPerformance.java | 4 +- .../sets/darkascension/ChillOfForeboding.java | 9 +-- .../sets/darkascension/CurseOfEchoes.java | 24 +++---- .../darkascension/IncreasingConfusion.java | 12 +--- .../sets/darkascension/TrackersInstincts.java | 22 +++---- .../dragonsmaze/MirkoVoskMindDrinker.java | 13 ++-- .../mage/sets/eventide/SanityGrinding.java | 22 +++---- .../mage/sets/gatecrash/BalustradeSpy.java | 2 +- .../sets/gatecrash/BorborygmosEnraged.java | 34 +++++----- .../sets/gatecrash/CoercedConfession.java | 16 ++--- .../sets/gatecrash/ConsumingAberration.java | 6 +- .../src/mage/sets/gatecrash/DimirCharm.java | 9 +-- .../src/mage/sets/gatecrash/MindGrind.java | 6 +- .../sets/gatecrash/UndercityInformer.java | 6 +- .../src/mage/sets/innistrad/ArmoredSkaab.java | 33 ---------- .../src/mage/sets/innistrad/CellarDoor.java | 2 +- .../mage/sets/innistrad/GhoulcallersBell.java | 2 +- .../sets/innistrad/HereticsPunishment.java | 22 +++---- .../src/mage/sets/innistrad/Mindshrieker.java | 21 +++--- .../sets/innistrad/MirrorMadPhantasm.java | 7 +- Mage.Sets/src/mage/sets/invasion/Void.java | 2 +- .../mage/sets/judgment/QuietSpeculation.java | 14 +--- .../sets/khansoftarkir/SidisiBroodTyrant.java | 42 +++--------- .../sets/khansoftarkir/SultaiSoothsayer.java | 2 +- .../src/mage/sets/magic2010/WarpWorld.java | 11 +--- .../mage/sets/magic2014/GlimpseTheFuture.java | 17 ++--- .../mage/sets/magic2014/JacesMindseeker.java | 38 ++++++----- .../sets/morningtide/CountrysideCrusher.java | 17 ++--- .../mage/sets/morningtide/NogginWhack.java | 3 +- .../newphyrexia/ChancellorOfTheSpires.java | 21 ++---- .../src/mage/sets/newphyrexia/Mindcrank.java | 26 ++++---- .../src/mage/sets/odyssey/ThinkTank.java | 14 ++-- .../mage/sets/prophecy/InfernalGenesis.java | 8 +-- .../returntoravnica/DestroyTheEvidence.java | 2 +- .../sets/returntoravnica/EpicExperiment.java | 2 +- .../mage/sets/returntoravnica/GuildFeud.java | 35 +++------- .../sets/returntoravnica/JaradsOrders.java | 26 ++++---- .../sets/returntoravnica/PsychicSpiral.java | 9 +-- .../sets/riseoftheeldrazi/KeeningStone.java | 15 +---- .../saviorsofkamigawa/ChoiceOfDamnations.java | 7 +- .../saviorsofkamigawa/CloudhoofKirin.java | 14 +--- .../scarsofmirrodin/GethLordOfTheVault.java | 32 ++-------- .../mage/sets/scarsofmirrodin/Grindclock.java | 16 ++--- .../mage/sets/scarsofmirrodin/MimicVat.java | 9 +-- .../seventhedition/AncestralMemories.java | 5 +- .../sets/shardsofalara/CorpseConnoisseur.java | 23 +++---- .../mage/sets/tempest/AltarOfDementia.java | 29 ++++----- .../src/mage/sets/tempest/Intuition.java | 29 ++++----- Mage.Sets/src/mage/sets/tempest/WoodSage.java | 7 +- .../mage/sets/tenthedition/Traumatize.java | 3 +- .../src/mage/sets/urzassaga/Whetstone.java | 11 +--- .../cards/abilities/curses/CursesTest.java | 6 ++ .../cards/abilities/keywords/DredgeTest.java | 33 +++++++++- .../cards/abilities/keywords/ReboundTest.java | 16 ++++- ...utTopCardOfYourLibraryToGraveyardCost.java | 4 +- .../common/AddManaOfAnyColorEffect.java | 2 +- .../CounterTargetWithReplacementEffect.java | 5 +- .../PutLibraryIntoGraveTargetEffect.java | 4 +- ...ardOfLibraryIntoGraveControllerEffect.java | 3 +- ...ardOfLibraryIntoGraveEachPlayerEffect.java | 9 +-- .../effects/common/ReturnFromExileEffect.java | 5 +- .../mage/abilities/keyword/DredgeAbility.java | 4 +- Mage/src/mage/game/GameImpl.java | 5 +- Mage/src/mage/game/events/GameEvent.java | 3 +- .../src/mage/game/events/ZoneChangeEvent.java | 2 +- .../game/events/ZoneChangeGroupEvent.java | 64 +++++++++++++++++++ Mage/src/mage/game/stack/Spell.java | 17 +++-- Mage/src/mage/players/Player.java | 7 +- Mage/src/mage/players/PlayerImpl.java | 32 +++++----- .../common/TargetPermanentOrPlayer.java | 2 +- .../TargetPermanentOrPlayerWithCounter.java | 1 - 79 files changed, 490 insertions(+), 653 deletions(-) create mode 100644 Mage/src/mage/game/events/ZoneChangeGroupEvent.java diff --git a/Mage.Sets/src/mage/sets/alarareborn/EtherwroughtPage.java b/Mage.Sets/src/mage/sets/alarareborn/EtherwroughtPage.java index 5bd5abe27c2..7e3d9fb67f1 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/EtherwroughtPage.java +++ b/Mage.Sets/src/mage/sets/alarareborn/EtherwroughtPage.java @@ -104,15 +104,15 @@ class EtherwroughtPageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you != null && you.getLibrary().size() > 0) { - Card card = you.getLibrary().getFromTop(game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && controller.getLibrary().size() > 0) { + Card card = controller.getLibrary().getFromTop(game); if (card != null) { CardsImpl cards = new CardsImpl(); cards.add(card); - you.lookAtCards("Etherwrought Page", cards, game); - if (you.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", game)) { - return card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + controller.lookAtCards("Etherwrought Page", cards, game); + if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", game)) { + return controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/alarareborn/MindFuneral.java b/Mage.Sets/src/mage/sets/alarareborn/MindFuneral.java index 02831131bf2..42d86adc3f0 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/MindFuneral.java +++ b/Mage.Sets/src/mage/sets/alarareborn/MindFuneral.java @@ -104,9 +104,7 @@ class MindFuneralEffect extends OneShotEffect { cards.add(card); } opponent.revealCards("Mind Funeral", cards, game); - for (Card card: cards.getCards(game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } + opponent.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } diff --git a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java index de68c7230aa..6f3768f2913 100644 --- a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java +++ b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java @@ -55,7 +55,7 @@ public class HelmOfObedience extends CardImpl { super(ownerId, 163, "Helm of Obedience", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); this.expansionSetCode = "ALL"; - // {X}, {tap}: Target opponent puts cards from the top of his or her library into his or her graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. + // {X}, {T}: Target opponent puts cards from the top of his or her library into his or her graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. VariableManaCost xCosts = new VariableManaCost(); xCosts.setMinX(1); SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HelmOfObedienceEffect(), xCosts); @@ -106,28 +106,26 @@ class HelmOfObedienceEffect extends OneShotEffect { while(player.getLibrary().size() > 0) { Card card = player.getLibrary().removeFromTop(game); if (card != null){ - if(card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false)){ - if(card.getCardType().contains(CardType.CREATURE)){ - // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience - // and put that card onto the battlefield under your control. - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - sourcePermanent.sacrifice(source.getSourceId(), game); - } - if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { - card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); - } + player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + if(card.getCardType().contains(CardType.CREATURE)){ + // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience + // and put that card onto the battlefield under your control. + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null) { + sourcePermanent.sacrifice(source.getSourceId(), game); + } + if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { + card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + } + break; + } + else{ + numberOfCard++; + if(numberOfCard >= max){ break; } - else{ - numberOfCard++; - if(numberOfCard >= max){ - break; - } - } } - } - else{ + } else{ return false; } } diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SatyrWayfinder.java b/Mage.Sets/src/mage/sets/bornofthegods/SatyrWayfinder.java index 8183e6d565d..64eb989e36f 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SatyrWayfinder.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SatyrWayfinder.java @@ -94,32 +94,26 @@ class SatyrWayfinderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (player != null && sourceObject != null) { + if (controller != null && sourceObject != null) { Cards cards = new CardsImpl(Zone.LIBRARY); - cards.addAll(player.getLibrary().getTopCards(game, 4)); + cards.addAll(controller.getLibrary().getTopCards(game, 4)); boolean properCardFound = cards.count(filterPutInHand, source.getControllerId(), source.getSourceId(), game) > 0; if (!cards.isEmpty()) { - player.revealCards(sourceObject.getName(), cards, game); + controller.revealCards(sourceObject.getName(), cards, game); TargetCard target = new TargetCard(Zone.LIBRARY, filterPutInHand); if (properCardFound && - player.chooseUse(outcome, "Put a land card into your hand?", game) && - player.choose(Outcome.DrawCard, cards, target, game)) { + controller.chooseUse(outcome, "Put a land card into your hand?", game) && + controller.choose(Outcome.DrawCard, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { cards.remove(card); - player.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.LIBRARY); + controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.LIBRARY); } } - - for (UUID cardId : cards) { - Card card = game.getCard(cardId); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/InameDeathAspect.java b/Mage.Sets/src/mage/sets/championsofkamigawa/InameDeathAspect.java index 5500eadf0b0..238a54f0116 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/InameDeathAspect.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/InameDeathAspect.java @@ -37,6 +37,8 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.SearchEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; @@ -99,12 +101,7 @@ class InameDeathAspectEffect extends SearchEffect { Player player = game.getPlayer(source.getControllerId()); if (player != null && player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + player.moveCards(new CardsImpl(target.getTargets()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } player.shuffleLibrary(game); return true; diff --git a/Mage.Sets/src/mage/sets/commander/BuriedAlive.java b/Mage.Sets/src/mage/sets/commander/BuriedAlive.java index 9126027ae64..d22057fca8e 100644 --- a/Mage.Sets/src/mage/sets/commander/BuriedAlive.java +++ b/Mage.Sets/src/mage/sets/commander/BuriedAlive.java @@ -27,12 +27,13 @@ */ package mage.sets.commander; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.SearchEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; @@ -87,19 +88,12 @@ class BuriedAliveEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (player.searchLibrary(target, game)) { - if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + if (controller.searchLibrary(target, game)) { + controller.moveCards(new CardsImpl(target.getTargets()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/commander/SzadekLordOfSecrets.java b/Mage.Sets/src/mage/sets/commander/SzadekLordOfSecrets.java index 8bf11ef6124..63583b4c80d 100644 --- a/Mage.Sets/src/mage/sets/commander/SzadekLordOfSecrets.java +++ b/Mage.Sets/src/mage/sets/commander/SzadekLordOfSecrets.java @@ -84,11 +84,9 @@ public class SzadekLordOfSecrets extends CardImpl { class SzadekLordOfSecretsEffect extends ReplacementEffectImpl { - List cards; - SzadekLordOfSecretsEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on Szadek and that player puts that many cards from the top of his or her library into his or her graveyard"; + staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on {this} and that player puts that many cards from the top of his or her library into his or her graveyard"; } SzadekLordOfSecretsEffect(final SzadekLordOfSecretsEffect effect) { @@ -101,24 +99,25 @@ class SzadekLordOfSecretsEffect extends ReplacementEffectImpl { Player damagedPlayer = game.getPlayer(damageEvent.getTargetId()); if (damageEvent.isCombatDamage()) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { - p.addCounters(CounterType.P1P1.createInstance(damageEvent.getAmount()), game); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(damageEvent.getAmount()), game); if (damagedPlayer != null) { - cards = damagedPlayer.getLibrary().getTopCards(game, damageEvent.getAmount()); - } - for (Card card : cards) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + damagedPlayer.moveCards(damagedPlayer.getLibrary().getTopCards(game, damageEvent.getAmount()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } } return true; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGE_PLAYER - && event.getSourceId().equals(source.getSourceId())) { + if (event.getSourceId().equals(source.getSourceId())) { DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; if (damageEvent.isCombatDamage()) { return true; diff --git a/Mage.Sets/src/mage/sets/commander2013/StrategicPlanning.java b/Mage.Sets/src/mage/sets/commander2013/StrategicPlanning.java index 47e25463450..44aa83956a7 100644 --- a/Mage.Sets/src/mage/sets/commander2013/StrategicPlanning.java +++ b/Mage.Sets/src/mage/sets/commander2013/StrategicPlanning.java @@ -86,34 +86,22 @@ class StrategicPlanningEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - - if (player != null) { - Cards cards = new CardsImpl(Zone.PICK); - int cardsCount = Math.min(3, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - cards.add(card); - game.setZone(card.getId(), Zone.PICK); - } - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Cards cards = new CardsImpl(); + cards.addAll(controller.getLibrary().getTopCards(game, 3)); if (cards.size() > 0) { - player.lookAtCards("Strategic Planning", cards, game); - - TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put in your hand")); - if (player.choose(Outcome.Benefit, cards, target, game)) { + controller.lookAtCards("Strategic Planning", cards, game); + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put in your hand")); + if (controller.choose(Outcome.Benefit, cards, target, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); cards.remove(card); } } - - for (Card card : cards.getCards(game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/conflux/TeleminPerformance.java b/Mage.Sets/src/mage/sets/conflux/TeleminPerformance.java index e816af5123b..ce27e531d29 100644 --- a/Mage.Sets/src/mage/sets/conflux/TeleminPerformance.java +++ b/Mage.Sets/src/mage/sets/conflux/TeleminPerformance.java @@ -99,12 +99,12 @@ class TeleminPerformanceEffect extends OneShotEffect { } if (!creatureFound) { cards.add(card); - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); } } } - if (!cards.isEmpty()) { + if (!cards.isEmpty()) { opponent.revealCards("Telemin Performance", cards, game); + opponent.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } if (creature != null) { return creature.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/darkascension/ChillOfForeboding.java b/Mage.Sets/src/mage/sets/darkascension/ChillOfForeboding.java index 71a3915da55..88ee72413c7 100644 --- a/Mage.Sets/src/mage/sets/darkascension/ChillOfForeboding.java +++ b/Mage.Sets/src/mage/sets/darkascension/ChillOfForeboding.java @@ -53,7 +53,6 @@ public class ChillOfForeboding extends CardImpl { super(ownerId, 32, "Chill of Foreboding", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{U}"); this.expansionSetCode = "DKA"; - // Each player puts the top five cards of his or her library into his or her graveyard. this.getSpellAbility().addEffect(new ChillOfForebodingEffect()); // Flashback {7}{U} @@ -92,13 +91,7 @@ class ChillOfForebodingEffect extends OneShotEffect { for (UUID playerId : sourcePlayer.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { - int count = Math.min(5, player.getLibrary().size()); - for (int i = 0; i < count; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + player.moveCards(player.getLibrary().getTopCards(game, 5), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } return true; diff --git a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java index ccf4a8471f9..bc023ef8818 100644 --- a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java +++ b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java @@ -49,7 +49,6 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.TargetSpell; import mage.target.targetpointer.FixedTarget; /** @@ -108,18 +107,21 @@ class CurseOfEchoesCopyTriggeredAbility extends TriggeredAbilityImpl { return new CurseOfEchoesCopyTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY))) { - Permanent enchantment = game.getPermanent(sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && spell.getControllerId().equals(player.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); - return true; - } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY))) { + Permanent enchantment = game.getPermanent(sourceId); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Player player = game.getPlayer(enchantment.getAttachedTo()); + if (player != null && spell.getControllerId().equals(player.getId())) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); + return true; } } } diff --git a/Mage.Sets/src/mage/sets/darkascension/IncreasingConfusion.java b/Mage.Sets/src/mage/sets/darkascension/IncreasingConfusion.java index dbc05348e5a..b0408ee75bf 100644 --- a/Mage.Sets/src/mage/sets/darkascension/IncreasingConfusion.java +++ b/Mage.Sets/src/mage/sets/darkascension/IncreasingConfusion.java @@ -74,7 +74,7 @@ class IncreasingConfusionEffect extends OneShotEffect { public IncreasingConfusionEffect() { super(Outcome.Detriment); - staticText = "Target player puts the top X cards of his or her library into his or her graveyard. If Increasing Confusion was cast from a graveyard, that player puts twice that many cards into his or her graveyard instead"; + staticText = "Target player puts the top X cards of his or her library into his or her graveyard. If {this} was cast from a graveyard, that player puts twice that many cards into his or her graveyard instead"; } public IncreasingConfusionEffect(final IncreasingConfusionEffect effect) { @@ -91,15 +91,7 @@ class IncreasingConfusionEffect extends OneShotEffect { if (spell.getFromZone() == Zone.GRAVEYARD) { amount *= 2; } - Card card; - for (int i = 0; i < amount; i++) { - card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } else { - break; - } - } + player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } } diff --git a/Mage.Sets/src/mage/sets/darkascension/TrackersInstincts.java b/Mage.Sets/src/mage/sets/darkascension/TrackersInstincts.java index 0deae9fcce5..689763b79f5 100644 --- a/Mage.Sets/src/mage/sets/darkascension/TrackersInstincts.java +++ b/Mage.Sets/src/mage/sets/darkascension/TrackersInstincts.java @@ -91,14 +91,14 @@ class TrackersInstinctsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { Cards cards = new CardsImpl(Zone.PICK); boolean creaturesFound = false; - int count = Math.min(player.getLibrary().size(), 4); + int count = Math.min(controller.getLibrary().size(), 4); for (int i = 0; i < count; i++) { - Card card = player.getLibrary().removeFromTop(game); + Card card = controller.getLibrary().removeFromTop(game); if (card != null) { cards.add(card); if (card.getCardType().contains(CardType.CREATURE)) { @@ -108,9 +108,9 @@ class TrackersInstinctsEffect extends OneShotEffect { } if (!cards.isEmpty()) { - player.revealCards("Tracker's Instincts", cards, game); - TargetCard target = new TargetCard(Zone.PICK, new FilterCreatureCard("creature card to put in hand")); - if (creaturesFound && player.choose(Outcome.DrawCard, cards, target, game)) { + controller.revealCards("Tracker's Instincts", cards, game); + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCreatureCard("creature card to put in hand")); + if (creaturesFound && controller.choose(Outcome.DrawCard, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { cards.remove(card); @@ -118,13 +118,7 @@ class TrackersInstinctsEffect extends OneShotEffect { } } - - for (UUID cardId : cards) { - Card card = game.getCard(cardId); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/MirkoVoskMindDrinker.java b/Mage.Sets/src/mage/sets/dragonsmaze/MirkoVoskMindDrinker.java index b599da5ac5b..456ae3f48e4 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/MirkoVoskMindDrinker.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/MirkoVoskMindDrinker.java @@ -34,6 +34,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -99,8 +100,8 @@ class MirkoVoskMindDrinkerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - Card sourceCard = game.getCard(source.getSourceId()); - if (player == null || sourceCard == null) { + MageObject sourceObject = source.getSourceObject(game); + if (player == null || sourceObject == null) { return false; } int landsToReveal = 4; @@ -117,12 +118,8 @@ class MirkoVoskMindDrinkerEffect extends OneShotEffect { } } } - player.revealCards("by " + sourceCard.getName() + " from " + player.getName(), cards, game); - for(Card card : cards.getCards(game)){ - if(card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + player.revealCards("by " + sourceObject.getName() + " from " + player.getName(), cards, game); + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } } diff --git a/Mage.Sets/src/mage/sets/eventide/SanityGrinding.java b/Mage.Sets/src/mage/sets/eventide/SanityGrinding.java index 9f5561d1c18..063228b681a 100644 --- a/Mage.Sets/src/mage/sets/eventide/SanityGrinding.java +++ b/Mage.Sets/src/mage/sets/eventide/SanityGrinding.java @@ -77,7 +77,7 @@ class SanityGrindingEffect extends OneShotEffect { public SanityGrindingEffect() { super(Outcome.Neutral); - staticText = "Chroma - Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of his or her library into his or her graveyard. Then put the cards you revealed this way on the bottom of your library in any order"; + staticText = "Chroma — Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of his or her library into his or her graveyard. Then put the cards you revealed this way on the bottom of your library in any order"; } public SanityGrindingEffect(final SanityGrindingEffect effect) { @@ -87,24 +87,22 @@ class SanityGrindingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetOpponent = game.getPlayer(source.getFirstTarget()); - Player you = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); Cards revealed = new CardsImpl(); int amount; - if (you == null) { + if (controller == null) { return false; } - amount = (Math.min(10, you.getLibrary().size())); + amount = (Math.min(10, controller.getLibrary().size())); for (int i = 0; i < amount; i++) { - revealed.add(you.getLibrary().removeFromTop(game)); + revealed.add(controller.getLibrary().removeFromTop(game)); } - you.revealCards("Sanity Grinding", revealed, game); + controller.revealCards("Sanity Grinding", revealed, game); if (targetOpponent != null) { - amount = (Math.min(targetOpponent.getLibrary().size(), new ChromaSanityGrindingCount(revealed).calculate(game, source, this))); - for (int i = 0; i < amount; i++) { - targetOpponent.getLibrary().removeFromTop(game).moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } + targetOpponent.moveCards(targetOpponent.getLibrary().getTopCards(game, new ChromaSanityGrindingCount(revealed).calculate(game, source, this)), + Zone.LIBRARY, Zone.GRAVEYARD, source, game); } - return you.putCardsOnBottomOfLibrary(revealed, game, source, true); + return controller.putCardsOnBottomOfLibrary(revealed, game, source, true); } @Override @@ -115,7 +113,7 @@ class SanityGrindingEffect extends OneShotEffect { class ChromaSanityGrindingCount implements DynamicValue { - private Cards revealed; + private final Cards revealed; public ChromaSanityGrindingCount(Cards revealed) { this.revealed = revealed; diff --git a/Mage.Sets/src/mage/sets/gatecrash/BalustradeSpy.java b/Mage.Sets/src/mage/sets/gatecrash/BalustradeSpy.java index 7cb7f38bff1..47ea519a111 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/BalustradeSpy.java +++ b/Mage.Sets/src/mage/sets/gatecrash/BalustradeSpy.java @@ -114,8 +114,8 @@ class BalustradeSpyEffect extends OneShotEffect { } } if (!cards.isEmpty()) { - controller.moveCardsToGraveyardWithInfo(cards, source, game, Zone.LIBRARY); controller.revealCards(sourceObject.getName(), cards, game); + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } return true; diff --git a/Mage.Sets/src/mage/sets/gatecrash/BorborygmosEnraged.java b/Mage.Sets/src/mage/sets/gatecrash/BorborygmosEnraged.java index 6d3728184cb..82190ce51ec 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/BorborygmosEnraged.java +++ b/Mage.Sets/src/mage/sets/gatecrash/BorborygmosEnraged.java @@ -108,27 +108,23 @@ class BorborygmosEnragedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Cards cards = new CardsImpl(Zone.PICK); - int count = Math.min(player.getLibrary().size(), 3); - for (int i = 0; i < count; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - cards.add(card); - game.setZone(card.getId(), Zone.PICK); - if (card.getCardType().contains(CardType.LAND)) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, true); - } else { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } - } - + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Cards cards = new CardsImpl(); + cards.addAll(controller.getLibrary().getTopCards(game, 3)); if (!cards.isEmpty()) { - player.revealCards("Borborygmous Enraged", cards, game); - return true; + controller.revealCards("Borborygmous Enraged", cards, game); + Cards landCards = new CardsImpl(); + for(Card card: cards.getCards(game) ) { + if (card.getCardType().contains(CardType.LAND)) { + landCards.add(card); + cards.remove(card); + } + } + controller.moveCards(landCards, Zone.LIBRARY, Zone.HAND, source, game); + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/gatecrash/CoercedConfession.java b/Mage.Sets/src/mage/sets/gatecrash/CoercedConfession.java index 59c989bcd71..77ab240e672 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/CoercedConfession.java +++ b/Mage.Sets/src/mage/sets/gatecrash/CoercedConfession.java @@ -36,6 +36,8 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -88,16 +90,14 @@ class CoercedConfessionMillEffect extends OneShotEffect { Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player != null) { int foundCreatures = 0; - int cardsCount = Math.min(4, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - if (card.getCardType().contains(CardType.CREATURE)) { - ++foundCreatures; - } - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + Cards cards = new CardsImpl(); + for(Card card: player.getLibrary().getTopCards(game, 4)) { + cards.add(card); + if (card.getCardType().contains(CardType.CREATURE)) { + ++foundCreatures; } } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); if (foundCreatures > 0) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/sets/gatecrash/ConsumingAberration.java b/Mage.Sets/src/mage/sets/gatecrash/ConsumingAberration.java index 13dafeafa36..6e63fe57ac9 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/ConsumingAberration.java +++ b/Mage.Sets/src/mage/sets/gatecrash/ConsumingAberration.java @@ -116,11 +116,7 @@ class ConsumingAberrationEffect extends OneShotEffect { } } player.revealCards("Consuming Aberrtion", cards, game); - for(Card card : cards.getCards(game)){ - if(card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/gatecrash/DimirCharm.java b/Mage.Sets/src/mage/sets/gatecrash/DimirCharm.java index 3a73a2c04be..eca00ed3f00 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/DimirCharm.java +++ b/Mage.Sets/src/mage/sets/gatecrash/DimirCharm.java @@ -123,19 +123,14 @@ class DimirCharmEffect extends OneShotEffect { } } if(cards.size() > 0){ - TargetCard target = new TargetCard(Zone.PICK, new FilterCard("Card to put back on top of library")); + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("Card to put back on top of library")); if(controller.chooseTarget(Outcome.Benefit, cards, target, source, game)){ Card card = cards.get(target.getFirstTarget(), game); if(card != null){ card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); cards.remove(card); } - - for(Card card2 : cards.getCards(game)){ - if(card2 != null){ - card2.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } } diff --git a/Mage.Sets/src/mage/sets/gatecrash/MindGrind.java b/Mage.Sets/src/mage/sets/gatecrash/MindGrind.java index 3cd85a56342..1602e9e94bc 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/MindGrind.java +++ b/Mage.Sets/src/mage/sets/gatecrash/MindGrind.java @@ -117,11 +117,7 @@ class MindGrindEffect extends OneShotEffect { } } player.revealCards("by " + sourceCard.getName() + " from " + player.getName(), cards, game); - for(Card card : cards.getCards(game)){ - if(card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/gatecrash/UndercityInformer.java b/Mage.Sets/src/mage/sets/gatecrash/UndercityInformer.java index 39953838c5e..bb2a76556b0 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/UndercityInformer.java +++ b/Mage.Sets/src/mage/sets/gatecrash/UndercityInformer.java @@ -117,11 +117,7 @@ class UndercityInformerEffect extends OneShotEffect { } } player.revealCards("Undercity Informer", cards, game); - for(Card card : cards.getCards(game)){ - if(card != null){ - card.moveToZone(Zone.GRAVEYARD, id, game, true); - } - } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } } diff --git a/Mage.Sets/src/mage/sets/innistrad/ArmoredSkaab.java b/Mage.Sets/src/mage/sets/innistrad/ArmoredSkaab.java index ce6145d3c42..e6fed77ca63 100644 --- a/Mage.Sets/src/mage/sets/innistrad/ArmoredSkaab.java +++ b/Mage.Sets/src/mage/sets/innistrad/ArmoredSkaab.java @@ -70,36 +70,3 @@ public class ArmoredSkaab extends CardImpl { return new ArmoredSkaab(this); } } - -class ArmoredSkaabEffect extends OneShotEffect { - - public ArmoredSkaabEffect() { - super(Outcome.Discard); - this.staticText = "put the top four cards of your library into your graveyard"; - } - - public ArmoredSkaabEffect(final ArmoredSkaabEffect effect) { - super(effect); - } - - @Override - public ArmoredSkaabEffect copy() { - return new ArmoredSkaabEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int cardsCount = Math.min(4, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/innistrad/CellarDoor.java b/Mage.Sets/src/mage/sets/innistrad/CellarDoor.java index 29421c637e8..e209ee7f2d4 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CellarDoor.java +++ b/Mage.Sets/src/mage/sets/innistrad/CellarDoor.java @@ -93,7 +93,7 @@ class CellarDoorEffect extends OneShotEffect { if (player != null && player.getLibrary().size() > 0) { Card card = player.getLibrary().removeFromBottom(game); if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); if (card.getCardType().contains(CardType.CREATURE)) { ZombieToken token = new ZombieToken("ISD"); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/innistrad/GhoulcallersBell.java b/Mage.Sets/src/mage/sets/innistrad/GhoulcallersBell.java index 114c5ced4a2..42baa5e118f 100644 --- a/Mage.Sets/src/mage/sets/innistrad/GhoulcallersBell.java +++ b/Mage.Sets/src/mage/sets/innistrad/GhoulcallersBell.java @@ -89,7 +89,7 @@ class GhoulcallersBellEffect extends OneShotEffect { if (player.getLibrary().size() > 0) { Card card = player.getLibrary().removeFromTop(game); if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } } diff --git a/Mage.Sets/src/mage/sets/innistrad/HereticsPunishment.java b/Mage.Sets/src/mage/sets/innistrad/HereticsPunishment.java index b58d40d6e88..bfd17fe119d 100644 --- a/Mage.Sets/src/mage/sets/innistrad/HereticsPunishment.java +++ b/Mage.Sets/src/mage/sets/innistrad/HereticsPunishment.java @@ -27,6 +27,7 @@ */ package mage.sets.innistrad; +import java.util.List; import java.util.UUID; import mage.constants.CardType; import mage.constants.Outcome; @@ -74,7 +75,7 @@ class HereticsPunishmentEffect extends OneShotEffect { public HereticsPunishmentEffect() { super(Outcome.Damage); - staticText = "Choose target creature or player, then put the top three cards of your library into your graveyard. Heretic's Punishment deals damage to that creature or player equal to the highest converted mana cost among those cards"; + staticText = "Choose target creature or player, then put the top three cards of your library into your graveyard. {this} deals damage to that creature or player equal to the highest converted mana cost among those cards"; } public HereticsPunishmentEffect(final HereticsPunishmentEffect effect) { @@ -83,18 +84,17 @@ class HereticsPunishmentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { int maxCost = 0; - for (int i = 0; i < Math.min(3, player.getLibrary().size()); i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - int test = card.getManaCost().convertedManaCost(); - if (test > maxCost) - maxCost = test; - } + List cardList = controller.getLibrary().getTopCards(game, 3); + for (Card card: cardList) { + int test = card.getManaCost().convertedManaCost(); + if (test > maxCost) { + maxCost = test; + } } + controller.moveCards(cardList, Zone.LIBRARY, Zone.GRAVEYARD, source, game); Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); if (permanent != null) { permanent.damage(maxCost, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/sets/innistrad/Mindshrieker.java b/Mage.Sets/src/mage/sets/innistrad/Mindshrieker.java index d2566c89f20..563107df19f 100644 --- a/Mage.Sets/src/mage/sets/innistrad/Mindshrieker.java +++ b/Mage.Sets/src/mage/sets/innistrad/Mindshrieker.java @@ -92,16 +92,19 @@ class MindshriekerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null && player.getLibrary().size() > 0) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - int amount = card.getManaCost().convertedManaCost(); - if (amount > 0) - game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source); - return true; + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + if (targetPlayer.getLibrary().size() > 0) { + Card card = targetPlayer.getLibrary().removeFromTop(game); + if (card != null) { + targetPlayer.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + int amount = card.getManaCost().convertedManaCost(); + if (amount > 0) { + game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source); + } + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/innistrad/MirrorMadPhantasm.java b/Mage.Sets/src/mage/sets/innistrad/MirrorMadPhantasm.java index d6156710c65..6b54330e693 100644 --- a/Mage.Sets/src/mage/sets/innistrad/MirrorMadPhantasm.java +++ b/Mage.Sets/src/mage/sets/innistrad/MirrorMadPhantasm.java @@ -98,8 +98,9 @@ class MirrorMadPhantasmEffect extends OneShotEffect { Cards cards = new CardsImpl(); while (true) { Card card = player.getLibrary().removeFromTop(game); - if (card == null) + if (card == null) { break; + } if (card.getName().equals("Mirror-Mad Phantasm")) { card.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), player.getId()); break; @@ -107,9 +108,7 @@ class MirrorMadPhantasmEffect extends OneShotEffect { cards.add(card); } player.revealCards("Mirror-Mad Phantasm", cards, game); - for (Card card: cards.getCards(game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } } diff --git a/Mage.Sets/src/mage/sets/invasion/Void.java b/Mage.Sets/src/mage/sets/invasion/Void.java index 987da536e67..b6a682cb114 100644 --- a/Mage.Sets/src/mage/sets/invasion/Void.java +++ b/Mage.Sets/src/mage/sets/invasion/Void.java @@ -119,7 +119,7 @@ class VoidEffect extends OneShotEffect { targetPlayer.revealCards("Void", targetPlayer.getHand(), game); for (Card card : targetPlayer.getHand().getCards(game)) { if (filterCard.match(card, game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + targetPlayer.discard(card, source, game); } } } else { diff --git a/Mage.Sets/src/mage/sets/judgment/QuietSpeculation.java b/Mage.Sets/src/mage/sets/judgment/QuietSpeculation.java index a5dff4aa08e..0891389430b 100644 --- a/Mage.Sets/src/mage/sets/judgment/QuietSpeculation.java +++ b/Mage.Sets/src/mage/sets/judgment/QuietSpeculation.java @@ -105,17 +105,9 @@ class SearchLibraryPutInGraveEffect extends SearchEffect { } if (player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { - Cards cards = new CardsImpl(); - for (UUID cardId: (List)target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - cards.add(card); - } - } - if (cards.size() > 0) { - player.revealCards("Quiet Speculation", cards, game); - } + Cards cards = new CardsImpl(target.getTargets()); + player.revealCards("Quiet Speculation", cards, game); + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } player.shuffleLibrary(game); return true; diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SidisiBroodTyrant.java b/Mage.Sets/src/mage/sets/khansoftarkir/SidisiBroodTyrant.java index 190f4db7386..6eda6b2e4ea 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SidisiBroodTyrant.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SidisiBroodTyrant.java @@ -30,7 +30,6 @@ package mage.sets.khansoftarkir; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; import mage.cards.Card; @@ -41,9 +40,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; +import mage.game.events.ZoneChangeGroupEvent; import mage.game.permanent.token.ZombieToken; -import mage.game.stack.StackObject; /** * @@ -102,58 +100,38 @@ class SidisiBroodTyrantAbility extends TriggeredAbilityImpl { if (event.getType() == EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId())) { return true; } - if (event.getType() == EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId()) ) { - return true; - } - return false; + return event.getType() == EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId()); } @Override public String getRule() { return "Whenever {this} enters the battlefield or attacks, put the top three cards of your library into your graveyard."; } - } -class SidisiBroodTyrantTriggeredAbility extends ZoneChangeTriggeredAbility { - - UUID lastStackObjectId = null; +class SidisiBroodTyrantTriggeredAbility extends TriggeredAbilityImpl { public SidisiBroodTyrantTriggeredAbility() { - super(Zone.BATTLEFIELD, Zone.LIBRARY, Zone.GRAVEYARD, new CreateTokenEffect(new ZombieToken("KTK")), "", false); + super(Zone.BATTLEFIELD, new CreateTokenEffect(new ZombieToken("KTK")), false); } public SidisiBroodTyrantTriggeredAbility(final SidisiBroodTyrantTriggeredAbility ability) { super(ability); - this.lastStackObjectId = ability.lastStackObjectId; } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; + return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; } @Override public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; - if ((fromZone == null || zEvent.getFromZone() == fromZone) && (toZone == null || zEvent.getToZone() == toZone)) { - Card card = game.getCard(event.getTargetId()); - if (card != null && card.getOwnerId().equals(getControllerId()) && card.getCardType().contains(CardType.CREATURE)) { - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject == null) { - stackObject = (StackObject) game.getLastKnownInformation(event.getSourceId(), Zone.STACK); - } - // If multiple cards go to graveyard from replacement effect (e.g. Dredge) each card is wrongly handled as a new event - if (stackObject != null) { - if (stackObject.getId().equals(lastStackObjectId)) { - return false; // was already handled - } - lastStackObjectId = stackObject.getId(); + ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent)event; + if (Zone.LIBRARY == zEvent.getFromZone() && Zone.GRAVEYARD == zEvent.getToZone()) { + for (Card card: zEvent.getCards()) { + if (card.getOwnerId().equals(getControllerId()) && card.getCardType().contains(CardType.CREATURE)) { return true; - } else { - // special action or replacement effect, so we can't check yet if multiple cards are moved with one effect - return true; - } + } } } return false; diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiSoothsayer.java b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiSoothsayer.java index 2409babeeba..78089591ff5 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiSoothsayer.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiSoothsayer.java @@ -104,7 +104,7 @@ class SultaiSoothsayerEffect extends OneShotEffect { if (controller.choose(Outcome.Benefit, cards, target, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { - controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); cards.remove(card); } } diff --git a/Mage.Sets/src/mage/sets/magic2010/WarpWorld.java b/Mage.Sets/src/mage/sets/magic2010/WarpWorld.java index 252ec357935..ef4217d5967 100644 --- a/Mage.Sets/src/mage/sets/magic2010/WarpWorld.java +++ b/Mage.Sets/src/mage/sets/magic2010/WarpWorld.java @@ -180,17 +180,12 @@ class WarpWorldEffect extends OneShotEffect { player = playerList.getNext(game); } while (!player.getId().equals(game.getActivePlayerId())); - // put the rest of the cards into the graveyard + // put the rest of the cards on buttom of the library playerList.setCurrent(game.getActivePlayerId()); player = game.getPlayer(game.getActivePlayerId()); do { - CardsImpl cards = cardsRevealed.get(player.getId()); - for (Card card : cards.getCards(game)) { - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } - + CardsImpl cards = cardsRevealed.get(player.getId()); + player.putCardsOnBottomOfLibrary(cards, game, source, false); player = playerList.getNext(game); } while (!player.getId().equals(game.getActivePlayerId())); diff --git a/Mage.Sets/src/mage/sets/magic2014/GlimpseTheFuture.java b/Mage.Sets/src/mage/sets/magic2014/GlimpseTheFuture.java index 670830ddbc6..dedcd6f7bac 100644 --- a/Mage.Sets/src/mage/sets/magic2014/GlimpseTheFuture.java +++ b/Mage.Sets/src/mage/sets/magic2014/GlimpseTheFuture.java @@ -86,13 +86,13 @@ class GlimpseTheFutureEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); - if (player != null) { + if (controller != null) { Cards cards = new CardsImpl(Zone.PICK); - int cardsCount = Math.min(3, player.getLibrary().size()); + int cardsCount = Math.min(3, controller.getLibrary().size()); for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); + Card card = controller.getLibrary().removeFromTop(game); if (card != null) { cards.add(card); game.setZone(card.getId(), Zone.PICK); @@ -100,20 +100,17 @@ class GlimpseTheFutureEffect extends OneShotEffect { } if (cards.size() > 0) { - player.lookAtCards("Glimpse the Future", cards, game); + controller.lookAtCards("Glimpse the Future", cards, game); TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put in your hand")); - if (player.choose(Outcome.Benefit, cards, target, game)) { + if (controller.choose(Outcome.Benefit, cards, target, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { card.moveToZone(Zone.HAND, source.getSourceId(), game, false); cards.remove(card); } } - - for (Card card : cards.getCards(game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/magic2014/JacesMindseeker.java b/Mage.Sets/src/mage/sets/magic2014/JacesMindseeker.java index 615e47223df..2c7791d42d9 100644 --- a/Mage.Sets/src/mage/sets/magic2014/JacesMindseeker.java +++ b/Mage.Sets/src/mage/sets/magic2014/JacesMindseeker.java @@ -27,6 +27,7 @@ */ package mage.sets.magic2014; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -102,35 +103,32 @@ class JaceMindseekerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Cards cards = new CardsImpl(); - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - // putting cards to grave shouldn't end the game, so getting minimun available - int cardsCount = Math.min(5, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - if (filter.match(card, game)) { - cards.add(card); + Cards cardsToCast = new CardsImpl(); + Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (targetOpponent != null) { + List allCards = targetOpponent.getLibrary().getTopCards(game, 5); + targetOpponent.moveCards(allCards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + for(Card card : allCards) { + if (filter.match(card, game)) { + Zone zone = game.getState().getZone(card.getId()); + // If the five cards are put into a public zone such as exile instead of a graveyard (perhaps due to the ability of Rest in Peace), + // you can cast one of those instant or sorcery cards from that zone. + if (zone.equals(Zone.GRAVEYARD) || zone.equals(Zone.EXILED)) { + cardsToCast.add(card); } - } - else { - throw new IllegalArgumentException("couldn't get card from library"); - } + } } } // cast an instant or sorcery for free - // TODO: Check if card can also be cast if it doesn't end in the graveyard due to other active effects (LevelX2 08.07.2013). - if (cards.size() > 0) { + if (cardsToCast.size() > 0) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - TargetCard target = new TargetCard(Zone.PICK, filter); + TargetCard target = new TargetCard(Zone.GRAVEYARD, filter); // zone should be ignored here target.setNotTarget(true); if (controller.chooseUse(outcome, "Cast an instant or sorcery card from among them for free?", game) - && controller.choose(outcome, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); + && controller.choose(outcome, cardsToCast, target, game)) { + Card card = cardsToCast.get(target.getFirstTarget(), game); if (card != null) { controller.cast(card.getSpellAbility(), game, true); } diff --git a/Mage.Sets/src/mage/sets/morningtide/CountrysideCrusher.java b/Mage.Sets/src/mage/sets/morningtide/CountrysideCrusher.java index ff36da3be68..bc8f7d46c19 100644 --- a/Mage.Sets/src/mage/sets/morningtide/CountrysideCrusher.java +++ b/Mage.Sets/src/mage/sets/morningtide/CountrysideCrusher.java @@ -103,23 +103,20 @@ class CountrysideCrusherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (player != null && permanent != null) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null && sourcePermanent != null) { Cards cards = new CardsImpl(); - while (player.getLibrary().size() > 0) { - Card card = player.getLibrary().getFromTop(game); + while (controller.getLibrary().size() > 0) { + Card card = controller.getLibrary().getFromTop(game); cards.add(card); if (card.getCardType().contains(CardType.LAND)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } else { break; } } - player.revealCards(permanent.getName(), cards, game); + controller.revealCards(sourcePermanent.getName(), cards, game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/morningtide/NogginWhack.java b/Mage.Sets/src/mage/sets/morningtide/NogginWhack.java index 967793035b1..ec4352946f1 100644 --- a/Mage.Sets/src/mage/sets/morningtide/NogginWhack.java +++ b/Mage.Sets/src/mage/sets/morningtide/NogginWhack.java @@ -127,8 +127,7 @@ class NogginWhackEffect extends OneShotEffect { for (UUID cardId : (List) targetInHand.getTargets()) { Card card = game.getCard(cardId); if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - game.informPlayers(new StringBuilder(sourceCard.getName()).append(": Discarded card ").append(card.getName()).toString()); + controller.discard(card, source, game); } } } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ChancellorOfTheSpires.java b/Mage.Sets/src/mage/sets/newphyrexia/ChancellorOfTheSpires.java index d8fe63fd22a..0f7592fde23 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ChancellorOfTheSpires.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ChancellorOfTheSpires.java @@ -40,7 +40,6 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PlayTargetWithoutPayingManaEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; @@ -104,13 +103,10 @@ class ChancellorOfTheSpiresDelayedTriggeredAbility extends DelayedTriggeredAbili ChancellorOfTheSpiresDelayedTriggeredAbility(ChancellorOfTheSpiresDelayedTriggeredAbility ability) { super(ability); } - + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE) { - return true; - } - return false; + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; } @Override public ChancellorOfTheSpiresDelayedTriggeredAbility copy() { @@ -132,16 +128,9 @@ class ChancellorOfTheSpiresEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (UUID opponentId : game.getOpponents(source.getControllerId())) { - Player player = game.getPlayer(opponentId); - if (player != null) { - int cardsCount = Math.min(7, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - else - break; - } + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + opponent.moveCards(opponent.getLibrary().getTopCards(game, 7), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } return true; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java b/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java index 1b44aed77cc..56411b9a645 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java @@ -37,12 +37,12 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.players.Player; +import mage.target.targetpointer.FixedTarget; /** * @@ -82,13 +82,17 @@ class MindcrankTriggeredAbility extends TriggeredAbilityImpl { return new MindcrankTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.LOST_LIFE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { Set opponents = game.getOpponents(this.getControllerId()); - if (event.getType() == EventType.LOST_LIFE && opponents.contains(event.getPlayerId())) { + if (opponents.contains(event.getPlayerId())) { Effect effect = this.getEffects().get(0); - effect.setValue("targetPlayer", event.getPlayerId()); - effect.setValue("amount", event.getAmount()); + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); return true; } return false; @@ -117,21 +121,13 @@ class MindcrankEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID targetId = (UUID) getValue("targetPlayer"); - Player player = game.getPlayer(targetId); - if (player != null) { + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (targetPlayer != null) { Integer amount = (Integer) getValue("amount"); if (amount == null) { amount = 0; } - // putting cards to grave shouldn't end the game, so getting minimum available - amount = Math.min(amount, player.getLibrary().size()); - for (int i = 0; i < amount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/odyssey/ThinkTank.java b/Mage.Sets/src/mage/sets/odyssey/ThinkTank.java index 67d6fd8943c..dc8602ec6c8 100644 --- a/Mage.Sets/src/mage/sets/odyssey/ThinkTank.java +++ b/Mage.Sets/src/mage/sets/odyssey/ThinkTank.java @@ -89,16 +89,16 @@ class ThinkTankLookLibraryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - if (you.getLibrary().size() > 0) { - Card card = you.getLibrary().getFromTop(game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + if (controller.getLibrary().size() > 0) { + Card card = controller.getLibrary().getFromTop(game); if (card != null) { CardsImpl cards = new CardsImpl(); cards.add(card); - you.lookAtCards("Think Tank", cards, game); - if (you.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", game)) { - return card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + controller.lookAtCards("Think Tank", cards, game); + if (controller.chooseUse(Outcome.Neutral, "Do you wish to put the card into your graveyard?", game)) { + return controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } diff --git a/Mage.Sets/src/mage/sets/prophecy/InfernalGenesis.java b/Mage.Sets/src/mage/sets/prophecy/InfernalGenesis.java index 4b1b8fa29b2..166bde321cf 100644 --- a/Mage.Sets/src/mage/sets/prophecy/InfernalGenesis.java +++ b/Mage.Sets/src/mage/sets/prophecy/InfernalGenesis.java @@ -69,7 +69,7 @@ public class InfernalGenesis extends CardImpl { class InfernalGenesisEffect extends OneShotEffect { InfernalGenesisEffect() { - super(Outcome.BoostCreature); + super(Outcome.PutCreatureInPlay); staticText = "that player puts the top card of his or her library into his or her graveyard. Then he or she puts X 1/1 black Minion creature tokens onto the battlefield, where X is that card's converted mana cost"; } @@ -79,14 +79,14 @@ class InfernalGenesisEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { Card card = player.getLibrary().removeFromTop(game); if (card != null) { - if (card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false)) { + if (player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game)) { int cmc = card.getManaCost().convertedManaCost(); MinionToken token = new MinionToken(); - token.putOntoBattlefield(cmc, game, id, player.getId()); + token.putOntoBattlefield(cmc, game, source.getSourceId(), player.getId()); } } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/DestroyTheEvidence.java b/Mage.Sets/src/mage/sets/returntoravnica/DestroyTheEvidence.java index 8f4e552bf5e..590469bd589 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/DestroyTheEvidence.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/DestroyTheEvidence.java @@ -114,7 +114,7 @@ class DestroyTheEvidenceEffect extends OneShotEffect { } } player.revealCards(sourceObject.getName(), cards, game, true); - player.moveCardsToGraveyardWithInfo(cards, source, game, Zone.LIBRARY); + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java b/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java index 37e9c1443ff..1fdac6a7e3e 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java @@ -125,7 +125,7 @@ class EpicExperimentEffect extends OneShotEffect { // move cards not cast to graveyard ExileZone exile = game.getExile().getExileZone(source.getSourceId()); if (exile != null) { - controller.moveCardsToGraveyardWithInfo(exile, source, game, Zone.EXILED); + controller.moveCards(exile, Zone.EXILED, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GuildFeud.java b/Mage.Sets/src/mage/sets/returntoravnica/GuildFeud.java index 12f8b4e7cb3..1a89c7af155 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GuildFeud.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GuildFeud.java @@ -59,7 +59,6 @@ public class GuildFeud extends CardImpl { super(ownerId, 97, "Guild Feud", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{5}{R}"); this.expansionSetCode = "RTR"; - // At the beginning of your upkeep, target opponent reveals the top three cards // of his or her library, may put a creature card from among them onto the battlefield, // then puts the rest into his or her graveyard. You do the same with the top three @@ -101,27 +100,20 @@ class GuildFeudEffect extends OneShotEffect { if (opponent != null && controller != null) { for (int activePlayer = 0; activePlayer < 2; activePlayer++) { Player player = (activePlayer == 0? opponent : controller); - Cards topThreeCards = new CardsImpl(Zone.PICK); - for (int i = 0; i < 3; i++) { - if (player.getLibrary().size() > 0) { - Card topCard = player.getLibrary().removeFromTop(game); - if (topCard != null) { - topThreeCards.add(topCard); - } - } - } - player.revealCards(player.getLogName() + " top three library cards", topThreeCards, game); + Cards topThreeCards = new CardsImpl(); + topThreeCards.addAll(player.getLibrary().getTopCards(game, 3)); + player.revealCards(player.getName() + " top three library cards", topThreeCards, game); Card creatureToBattlefield; if (!topThreeCards.isEmpty()) { if (player.chooseUse(Outcome.PutCreatureInPlay, "Put a creature card among them to the battlefield?", game)) { - TargetCard target = new TargetCard(Zone.PICK, + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCreatureCard( "creature card to put on the battlefield")); if (player.choose(Outcome.PutCreatureInPlay, topThreeCards, target, game)) { creatureToBattlefield = topThreeCards.get(target.getFirstTarget(), game); if (creatureToBattlefield != null) { topThreeCards.remove(creatureToBattlefield); - if (creatureToBattlefield.putOntoBattlefield(game, Zone.PICK, + if (creatureToBattlefield.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), player.getId())) { game.informPlayers("Guild Feud: " + player.getLogName() + " put " + creatureToBattlefield.getName() + " to the battlefield"); if (activePlayer == 0) { @@ -133,23 +125,14 @@ class GuildFeudEffect extends OneShotEffect { } } } - - if (topThreeCards.size() > 0) { - - while (topThreeCards.size() > 0) { - Card card = topThreeCards.get(topThreeCards.iterator().next(), game); - topThreeCards.remove(card); - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + player.moveCards(topThreeCards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } // If two creatures are put onto the battlefield this way, those creatures fight each other if (opponentCreature != null && controllerCreature != null) { - int power = opponentCreature.getPower().getValue(); - opponentCreature.damage(controllerCreature.getPower().getValue(), source.getSourceId(), game, false, true); - controllerCreature.damage(power, source.getSourceId(), game, false, true); + opponentCreature.fight(controllerCreature, source, game); } + return true; } return false; } @@ -158,4 +141,4 @@ class GuildFeudEffect extends OneShotEffect { public GuildFeudEffect copy() { return new GuildFeudEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/returntoravnica/JaradsOrders.java b/Mage.Sets/src/mage/sets/returntoravnica/JaradsOrders.java index 9b1a5887ca0..adc5aa137cd 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/JaradsOrders.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/JaradsOrders.java @@ -57,7 +57,6 @@ public class JaradsOrders extends CardImpl { super(ownerId, 175, "Jarad's Orders", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{B}{G}"); this.expansionSetCode = "RTR"; - // Search your library for up to two creature cards and reveal them. Put one into your hand and the other into your graveyard. Then shuffle your library. this.getSpellAbility().addEffect(new JaradsOrdersEffect()); } @@ -91,36 +90,35 @@ class JaradsOrdersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { TargetCardInLibrary target = new TargetCardInLibrary(0, 2, new FilterCreatureCard("creature cards")); - if (player.searchLibrary(target, game)) { + if (controller.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { Cards revealed = new CardsImpl(); for (UUID cardId: (List)target.getTargets()) { - Card card = player.getLibrary().getCard(cardId, game); + Card card = controller.getLibrary().getCard(cardId, game); revealed.add(card); } - player.revealCards("Jarad's Orders", revealed, game); + controller.revealCards("Jarad's Orders", revealed, game); if (target.getTargets().size() == 2) { TargetCard target2 = new TargetCard(Zone.PICK, filter); - player.choose(Outcome.Benefit, revealed, target2, game); + controller.choose(Outcome.Benefit, revealed, target2, game); Card card = revealed.get(target2.getFirstTarget(), game); - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); revealed.remove(card); card = revealed.getCards(game).iterator().next(); - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - else if (target.getTargets().size() == 1) { + controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + } else if (target.getTargets().size() == 1) { Card card = revealed.getCards(game).iterator().next(); - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); } } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); } return false; diff --git a/Mage.Sets/src/mage/sets/returntoravnica/PsychicSpiral.java b/Mage.Sets/src/mage/sets/returntoravnica/PsychicSpiral.java index 67ae4f6ad97..d0ce2b59753 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/PsychicSpiral.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/PsychicSpiral.java @@ -90,14 +90,7 @@ class PsychicSpiralEffect extends OneShotEffect { if (cardsInGraveyard > 0) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); if (targetPlayer != null) { - for (int i = 0; i 0) { - Card card = targetPlayer.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } - } + targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, cardsInGraveyard), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } return true; diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/KeeningStone.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KeeningStone.java index cc5a39bd65a..5a17bb68387 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/KeeningStone.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KeeningStone.java @@ -91,19 +91,8 @@ class KeeningStoneEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { - Library library = player.getLibrary(); - - int amount = Math.min(player.getGraveyard().size(), library.size()); - for (int i = 0; i < amount; i++) { - Card card = library.removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } - - if (amount > 0) { - return true; - } + player.moveCards(player.getLibrary().getTopCards(game, player.getGraveyard().size()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java index 95a195ae04a..b654d9ed6ad 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java @@ -93,19 +93,18 @@ class ChoiceOfDamnationsEffect extends OneShotEffect { int amount = targetPlayer.getAmount(0, Integer.MAX_VALUE, "Chooses a number", game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - String sb = String.valueOf("Shall " + targetPlayer.getLogName() + " lose ") + Integer.toString(amount) + " life?"; - if (controller.chooseUse(outcome, sb, game)) { + if (controller.chooseUse(outcome, "Shall " + targetPlayer.getLogName() + " lose " + amount + " life?", game)) { targetPlayer.loseLife(amount, game); } else { int numberPermanents = game.getState().getBattlefield().countAll(new FilterPermanent(), targetPlayer.getId(), game); if (numberPermanents > amount) { int numberToSacrifice = numberPermanents - amount; - Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterControlledPermanent(), false); + Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterControlledPermanent("permanent you control to sacrifice"), false); targetPlayer.chooseTarget(Outcome.Sacrifice, target, source, game); for (UUID uuid : target.getTargets()) { Permanent permanent = game.getPermanent(uuid); if (permanent != null) { - permanent.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + permanent.sacrifice(source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/CloudhoofKirin.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/CloudhoofKirin.java index b3486d48e43..b85a63f7e61 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/CloudhoofKirin.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/CloudhoofKirin.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -107,19 +106,8 @@ class CloudhoofKirinEffect extends OneShotEffect { } } if (targetPlayer != null) { - int cardsCount = Math.min(spell.getConvertedManaCost(), targetPlayer.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = targetPlayer.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - else { - break; - } - } - return true; + return targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, spell.getConvertedManaCost()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } - } return false; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GethLordOfTheVault.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GethLordOfTheVault.java index a14d871ffc1..28f18c2e72b 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GethLordOfTheVault.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GethLordOfTheVault.java @@ -47,7 +47,6 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInOpponentsGraveyard; @@ -114,34 +113,15 @@ class GethLordOfTheVaultEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getFirstTarget()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - // if still in graveyard - if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { - Player player = game.getPlayer(card.getOwnerId()); - if (card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId())) { - - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - permanent.setTapped(true); - } - - int xvalue = card.getManaCost().convertedManaCost(); - int cardsCount = Math.min(xvalue, player.getLibrary().size()); - - for (int i = 0; i < cardsCount; i++) { - Card removedCard = player.getLibrary().getFromTop(game); - if (removedCard != null) { - removedCard.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } else { - break; - } - } - - } + card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId(), true); + Player player = game.getPlayer(card.getOwnerId()); + if (player != null) { + player.moveCards(player.getLibrary().getTopCards(game, card.getManaCost().convertedManaCost()), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } - return false; + return true; } @Override diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Grindclock.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Grindclock.java index 902321562d0..d5432dbd14e 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Grindclock.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Grindclock.java @@ -38,7 +38,6 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.Outcome; import mage.counters.CounterType; @@ -85,17 +84,12 @@ class GrindclockEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int amount = game.getPermanent(source.getSourceId()).getCounters().getCount(CounterType.CHARGE); - Player player = game.getPlayer(source.getFirstTarget()); - Card card; - for (int i = 0; i < amount; i++) { - card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } else { - break; - } + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.LIBRARY, Zone.GRAVEYARD, source, game); + return true; } - return true; + return false; } @Override diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java index 47c8fb0af19..53df62a3c52 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java @@ -53,6 +53,7 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.UUID; +import mage.players.Player; /** * @author nantuko @@ -128,7 +129,7 @@ class MimicVatTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with Mimic Vat to its owner's graveyard"; + return "Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with {this} to its owner's graveyard"; } } @@ -146,14 +147,14 @@ class MimicVatEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null || permanent == null) { return false; } - // return older cards to graveyard for (UUID imprinted : permanent.getImprinted()) { Card card = game.getCard(imprinted); - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + controller.moveCards(card, Zone.EXILED, Zone.GRAVEYARD, source, game); } permanent.clearImprinted(game); diff --git a/Mage.Sets/src/mage/sets/seventhedition/AncestralMemories.java b/Mage.Sets/src/mage/sets/seventhedition/AncestralMemories.java index a9903de426b..ad72c539aaa 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/AncestralMemories.java +++ b/Mage.Sets/src/mage/sets/seventhedition/AncestralMemories.java @@ -113,10 +113,7 @@ class AncestralMemoriesEffect extends OneShotEffect { } } } - - for (Card card : cards.getCards(game)) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } + player.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/CorpseConnoisseur.java b/Mage.Sets/src/mage/sets/shardsofalara/CorpseConnoisseur.java index 6c31bd267e2..adc0352046a 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/CorpseConnoisseur.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/CorpseConnoisseur.java @@ -98,24 +98,19 @@ class SearchLibraryPutInGraveyard extends SearchEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - if (player.searchLibrary(target, game)) { - if (target.getTargets().size() > 0) { - Cards cards = new CardsImpl(); - for (UUID cardId: (List)target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null){ - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + if (controller.searchLibrary(target, game)) { + if (target.getTargets().size() > 0) { + Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; - } - player.shuffleLibrary(game); + } return false; } diff --git a/Mage.Sets/src/mage/sets/tempest/AltarOfDementia.java b/Mage.Sets/src/mage/sets/tempest/AltarOfDementia.java index 057d22782de..fe123fcf933 100644 --- a/Mage.Sets/src/mage/sets/tempest/AltarOfDementia.java +++ b/Mage.Sets/src/mage/sets/tempest/AltarOfDementia.java @@ -93,26 +93,19 @@ class AltarOfDementiaEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int amount = 0; - for (Cost cost: source.getCosts()) { - if (cost instanceof SacrificeTargetCost && ((SacrificeTargetCost)cost).getPermanents().size() > 0) { - amount = ((SacrificeTargetCost)cost).getPermanents().get(0).getPower().getValue(); - break; - } - } - if (amount > 0) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - for (int i = 0; i 0) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - } + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null) { + int amount = 0; + for (Cost cost: source.getCosts()) { + if (cost instanceof SacrificeTargetCost && ((SacrificeTargetCost)cost).getPermanents().size() > 0) { + amount = ((SacrificeTargetCost)cost).getPermanents().get(0).getPower().getValue(); + break; } - return true; } + if (amount > 0) { + player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.LIBRARY, Zone.GRAVEYARD, source, game); + } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/tempest/Intuition.java b/Mage.Sets/src/mage/sets/tempest/Intuition.java index 88e4186bd80..c3434f60978 100644 --- a/Mage.Sets/src/mage/sets/tempest/Intuition.java +++ b/Mage.Sets/src/mage/sets/tempest/Intuition.java @@ -94,43 +94,42 @@ class IntuitionEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(source.getFirstTarget()); - if (player == null || opponent == null) + if (controller == null || opponent == null) return false; - if (player.getLibrary().size() >= 3 && player.searchLibrary(target, game)) { + if (controller.getLibrary().size() >= 3 && controller.searchLibrary(target, game)) { if (target.getTargets().size() == 3) { Cards cards = new CardsImpl(); for (UUID cardId: (List)target.getTargets()) { - Card card = player.getLibrary().getCard(cardId, game); + Card card = controller.getLibrary().getCard(cardId, game); if (card != null) { cards.add(card); } } - player.revealCards("Reveal", cards, game); + controller.revealCards("Reveal", cards, game); TargetCard targetCard = new TargetCard(Zone.PICK, new FilterCard()); - while(!opponent.choose(Outcome.Neutral, cards, targetCard, game)); + while(!opponent.choose(Outcome.Neutral, cards, targetCard, game)) { + if (!opponent.isInGame()) { + return false; + } + } Card card = cards.get(targetCard.getFirstTarget(), game); if (card != null) { cards.remove(card); - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); } - - for(UUID uuid : cards){ - card = cards.get(uuid, game); - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } - + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; } - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return false; } diff --git a/Mage.Sets/src/mage/sets/tempest/WoodSage.java b/Mage.Sets/src/mage/sets/tempest/WoodSage.java index 9c563be407e..867039608a8 100644 --- a/Mage.Sets/src/mage/sets/tempest/WoodSage.java +++ b/Mage.Sets/src/mage/sets/tempest/WoodSage.java @@ -114,16 +114,17 @@ class WoodSageEffect extends OneShotEffect { Cards cards = new CardsImpl(); cards.addAll(controller.getLibrary().getTopCards(game, 4)); - if (!cards.isEmpty()) { + Cards cardsToHand = new CardsImpl(); controller.revealCards(sourceObject.getName(), cards, game); for (Card card: cards.getCards(game)) { if (card.getName().equals(cardName)) { - controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, true); + cardsToHand.add(card); cards.remove(card); } } - controller.moveCardsToGraveyardWithInfo(cards, source, game, Zone.LIBRARY); + controller.moveCards(cardsToHand, Zone.LIBRARY, Zone.HAND, source, game); + controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return true; } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Traumatize.java b/Mage.Sets/src/mage/sets/tenthedition/Traumatize.java index ec18e91f68b..e4a78743236 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Traumatize.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Traumatize.java @@ -80,8 +80,7 @@ class TraumatizeEffect extends OneShotEffect { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { int amount = player.getLibrary().size() / 2; - player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, amount), source, game, Zone.LIBRARY); - return true; + return player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return false; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/Whetstone.java b/Mage.Sets/src/mage/sets/urzassaga/Whetstone.java index 76684a8a32f..0dc9de8d86b 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Whetstone.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Whetstone.java @@ -83,16 +83,7 @@ class WhetstoneEffect extends OneShotEffect { for (UUID playerId : game.getPlayerList()) { Player player = game.getPlayer(playerId); if (player != null) { - // putting cards to grave shouldn't end the game, so getting minimun available - int cardsCount = Math.min(2, player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, false); - } else { - break; - } - } + player.moveCards(player.getLibrary().getTopCards(game, 2), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index 9d7ec3c27f9..bf2e2bfe77e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -33,7 +33,11 @@ public class CursesTest extends CardTestPlayerBase { public void testCurseOfEchoes() { addCard(Zone.BATTLEFIELD, playerA, "Island", 5); addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + // Enchant player + // Whenever enchanted player casts an instant or sorcery spell, each other player may copy that + // spell and may choose new targets for the copy he or she controls. addCard(Zone.HAND, playerA, "Curse of Echoes"); + // Draw three cards. addCard(Zone.HAND, playerB, "Jace's Ingenuity"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Echoes", playerB); @@ -49,6 +53,8 @@ public class CursesTest extends CardTestPlayerBase { @Test public void testCurseOfExhaustion1() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + // Enchant player + // Enchanted player can't cast more than one spell each turn. addCard(Zone.HAND, playerA, "Curse of Exhaustion"); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); addCard(Zone.HAND, playerB, "Lightning Bolt", 2); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DredgeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DredgeTest.java index 741f53a10b9..b0082cfc630 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DredgeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DredgeTest.java @@ -45,7 +45,38 @@ public class DredgeTest extends CardTestPlayerBase { */ - /** + @Test + public void testSultaiSoothsayerWithSidisiBroodTyrant() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // Whenever Sidisi, Brood Tyrant enters the battlefield or attacks, put the top three cards of your library into your graveyard. + // Whenever one or more creature cards are put into your graveyard from your library, put a 2/2 black Zombie creature token onto the battlefield. + addCard(Zone.BATTLEFIELD, playerA, "Sidisi, Brood Tyrant"); + // When Sultai Soothsayer enters the battlefield, look at the top four cards of your library. + // Put one of them into your hand and the rest into your graveyard. + addCard(Zone.HAND, playerA, "Sultai Soothsayer"); + + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 5); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sultai Soothsayer"); + addTarget(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertHandCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 3); + + assertPermanentCount(playerA, "Zombie", 1); // May only be one creature + + } + + /** * Had a Sidisi, Brood Tyrant in play and dredge a Stinkweed Imp hitting 3 creatures. * and Sidisi triggered 3 times instead of just one. */ diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReboundTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReboundTest.java index 552e4d9693f..aeb340acb5d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReboundTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReboundTest.java @@ -10,7 +10,15 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * + * 702.87. Rebound + * 702.87a Rebound appears on some instants and sorceries. It represents a static ability that functions while + * the spell is on the stack and may create a delayed triggered ability. "Rebound" means "If this spell was cast + * from your hand, instead of putting it into your graveyard as it resolves, exile it and, at the beginning of + * your next upkeep, you may cast this card from exile without paying its mana cost." + * 702.87b Casting a card without paying its mana cost as the result of a rebound ability follows the rules for + * paying alternative costs in rules 601.2b and 601.2e–g. + * 702.87c Multiple instances of rebound on the same spell are redundant. + * * @author jeff */ public class ReboundTest extends CardTestPlayerBase{ @@ -104,6 +112,8 @@ public class ReboundTest extends CardTestPlayerBase{ addCard(Zone.BATTLEFIELD, playerA, "Island", 1); // Target creature gets +1/+0 until end of turn and is unblockable this turn. + // Rebound (If you cast this spell from your hand, exile it as it resolves. At the beginning of your next upkeep, + // you may cast this card from exile without paying its mana cost.) addCard(Zone.HAND, playerA, "Distortion Strike"); addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); @@ -113,12 +123,12 @@ public class ReboundTest extends CardTestPlayerBase{ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Distortion Strike", "Memnite"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Memnite","Distortion Strike"); - setStopAt(1, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); //check exile and graveyard assertGraveyardCount(playerB, "Lightning Bolt", 1); - assertGraveyardCount(playerA, "Distortion Strike", 1); assertGraveyardCount(playerA, "Memnite", 1); + assertGraveyardCount(playerA, "Distortion Strike", 1); } } diff --git a/Mage/src/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java b/Mage/src/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java index 8923b3079ed..bd097d47ab7 100644 --- a/Mage/src/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java +++ b/Mage/src/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java @@ -68,9 +68,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl { Player player = game.getPlayer(controllerId); if (player != null && player.getLibrary().size() >= numberOfCards) { paid = true; - Cards cards = new CardsImpl(); - cards.addAll(player.getLibrary().getTopCards(game, numberOfCards)); - player.moveCardsToGraveyardWithInfo(cards, ability, game, Zone.LIBRARY); + player.moveCards(player.getLibrary().getTopCards(game, numberOfCards), Zone.LIBRARY, Zone.GRAVEYARD, ability, game); } return paid; } diff --git a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java index ea14b2f8752..fb7ad296c42 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java @@ -69,7 +69,7 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - ChoiceColor choice = new ChoiceColor(false); + ChoiceColor choice = new ChoiceColor(true); if (controller.choose(outcome, choice, game)) { if (choice.getColor() == null) { diff --git a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java index fb88906c902..d23e2c8f2ff 100644 --- a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java @@ -94,9 +94,6 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { if (mageObject instanceof Card) { Card card = (Card) mageObject; switch (targetZone) { - case HAND: - controller.moveCardToHandWithInfo(card, sourceId, game, Zone.STACK); - break; case LIBRARY: controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.STACK, flag, true); break; @@ -104,7 +101,7 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { controller.moveCardToExileWithInfo(card, null, "", sourceId, game, Zone.STACK, true); break; default: - card.moveToZone(targetZone, sourceId, game, flag); + controller.moveCards(card, Zone.STACK, targetZone, source, game); } } else { return false; diff --git a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java index 5ac6c37a0ae..d81b6a574cd 100644 --- a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java @@ -74,9 +74,7 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player != null) { - // putting cards to grave shouldn't end the game, so getting minimun available - int cardsCount = Math.min(amount.calculate(game, source, this), player.getLibrary().size()); - player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, cardsCount), source, game, Zone.LIBRARY); + player.moveCards(player.getLibrary().getTopCards(game, amount.calculate(game, source, this)), Zone.LIBRARY, Zone.GRAVEYARD, source, game); return true; } return false; diff --git a/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java b/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java index 94836d9094f..65269ae1d92 100644 --- a/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java @@ -65,8 +65,7 @@ public class PutTopCardOfLibraryIntoGraveControllerEffect extends OneShotEffect public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.moveCardsToGraveyardWithInfo(controller.getLibrary().getTopCards(game, numberCards), source, game, Zone.LIBRARY); - return true; + return controller.moveCards(controller.getLibrary().getTopCards(game, numberCards), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } return false; } diff --git a/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java index 677c60d4e95..c52b88fff0a 100644 --- a/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; @@ -106,13 +105,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect private void putCardsToGravecard(UUID playerId, Ability source, Game game) { Player player = game.getPlayer(playerId); if (player != null) { - int cardsCount = Math.min(numberCards.calculate(game, source, this), player.getLibrary().size()); - for (int i = 0; i < cardsCount; i++) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); - } - } + player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.LIBRARY, Zone.GRAVEYARD, source, game); } } diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java index 25bb4868036..a3eeb0a2c13 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java @@ -100,11 +100,14 @@ public class ReturnFromExileEffect extends OneShotEffect { } break; case HAND: - controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.EXILED); + controller.moveCards(card, Zone.EXILED, Zone.HAND, source, game); break; case LIBRARY: controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, true, true); break; + case GRAVEYARD: + controller.moveCards(card, Zone.EXILED, Zone.GRAVEYARD, source, game); + break; default: card.moveToZone(zone, source.getSourceId(), game, tapped); if (!game.isSimulation()) { diff --git a/Mage/src/mage/abilities/keyword/DredgeAbility.java b/Mage/src/mage/abilities/keyword/DredgeAbility.java index 86c1baf5508..62f77f3ada2 100644 --- a/Mage/src/mage/abilities/keyword/DredgeAbility.java +++ b/Mage/src/mage/abilities/keyword/DredgeAbility.java @@ -104,8 +104,8 @@ class DredgeEffect extends ReplacementEffectImpl { } Cards cardsToGrave = new CardsImpl(); cardsToGrave.addAll(player.getLibrary().getTopCards(game, amount)); - player.moveCardsToGraveyardWithInfo(cardsToGrave, source, game, Zone.LIBRARY); - player.moveCardToHandWithInfo(sourceCard, source.getSourceId(), game, Zone.GRAVEYARD); + player.moveCards(cardsToGrave, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + player.moveCards(sourceCard, Zone.GRAVEYARD, Zone.HAND, source, game); return true; } return false; diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 9b85b5ca4e7..28c823f6446 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1204,7 +1204,7 @@ public abstract class GameImpl implements Game, Serializable { top.resolve(this); } finally { if (top != null) { - state.getStack().remove(top); + state.getStack().remove(top); // seems partly redundant because move card from stack to grave is already done and the stack removed rememberLKI(top.getSourceId(), Zone.STACK, top); if (!getTurn().isEndTurnRequested()) { while (state.hasSimultaneousEvents()) { @@ -1739,8 +1739,7 @@ public abstract class GameImpl implements Game, Serializable { boolean result = false; if (permanent.moveToZone(Zone.GRAVEYARD, null, this, false)) { if (!this.isSimulation()) { - this.informPlayers(new StringBuilder(permanent.getLogName()) - .append(" is put into graveyard from battlefield").toString()); + this.informPlayers(permanent.getLogName() + " is put into graveyard from battlefield"); } result = true; } diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 7862334c565..50e4ab491fd 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -81,13 +81,14 @@ public class GameEvent implements Serializable { //player events /* ZONE_CHANGE - targetId id of the zone chaning object + targetId id of the zone changing object sourceId sourceId of the ability with the object moving effect playerId controller of the moved object amount not used for this event flag not used for this event */ ZONE_CHANGE, + ZONE_CHANGE_GROUP, EMPTY_DRAW, DRAW_CARD, DREW_CARD, MIRACLE_CARD_REVEALED, diff --git a/Mage/src/mage/game/events/ZoneChangeEvent.java b/Mage/src/mage/game/events/ZoneChangeEvent.java index 14cea4ccc0a..e200e22652c 100644 --- a/Mage/src/mage/game/events/ZoneChangeEvent.java +++ b/Mage/src/mage/game/events/ZoneChangeEvent.java @@ -87,7 +87,7 @@ public class ZoneChangeEvent extends GameEvent { public ZoneChangeEvent(UUID targetId, UUID playerId, Zone fromZone, Zone toZone) { this(targetId, null, playerId, fromZone, toZone); } - + public Zone getFromZone() { return fromZone; } diff --git a/Mage/src/mage/game/events/ZoneChangeGroupEvent.java b/Mage/src/mage/game/events/ZoneChangeGroupEvent.java new file mode 100644 index 00000000000..adb3e8cbc4b --- /dev/null +++ b/Mage/src/mage/game/events/ZoneChangeGroupEvent.java @@ -0,0 +1,64 @@ +/* +* 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.events; + +import java.util.List; +import java.util.UUID; +import mage.cards.Card; +import mage.constants.Zone; + +/** + * + * @author LevelX2 + */ +public class ZoneChangeGroupEvent extends GameEvent { + + private final Zone fromZone; + private final Zone toZone; + private final List cards; + + public ZoneChangeGroupEvent(List cards, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) { + super(EventType.ZONE_CHANGE_GROUP, null, sourceId, playerId); + this.fromZone = fromZone; + this.toZone = toZone; + this.cards = cards; + } + + public Zone getFromZone() { + return fromZone; + } + + public Zone getToZone() { + return toZone; + } + + public List getCards() { + return cards; + } + +} diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 7a9821ed6cd..5065b9296b5 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -201,13 +201,14 @@ public class Spell implements StackObject, Card { result |= spellAbility.resolve(game); } } -// game.getState().handleSimultaneousEvent(game); -// game.resetShortLivingLKI(); index++; } } if (game.getState().getZone(card.getMainCard().getId()) == Zone.STACK) { - card.moveToZone(Zone.GRAVEYARD, ability.getSourceId(), game, false); + Player player = game.getPlayer(getControllerId()); + if (player != null) { + player.moveCards(card, Zone.STACK, Zone.GRAVEYARD, ability, game); + } } return result; } @@ -524,7 +525,15 @@ public class Spell implements StackObject, Card { public void counter(UUID sourceId, Game game) { this.countered = true; if (!isCopiedSpell()) { - card.moveToZone(Zone.GRAVEYARD, sourceId, game, false); + Player player = game.getPlayer(getControllerId()); + if (player != null) { + Ability counteringAbility = null; + MageObject counteringObject = game.getObject(sourceId); + if (counteringObject instanceof StackObject) { + counteringAbility = ((StackObject)counteringObject).getStackAbility(); + } + player.moveCards(card, Zone.STACK, Zone.GRAVEYARD, counteringAbility, game); + } } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index d2d4b7d0646..0e9adf9bd0e 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -502,10 +502,8 @@ public interface Player extends MageItem, Copyable { /** - * Moves 1 to n cards to the graveyard. The owner of the cards may determine the order, - * if more than one card is moved to graveyard. - * Uses card.moveToZone and posts a inform message about moving the card to graveyard - * into the game log + * Internal used to move cards + * Use commonly player.moveCards() * * @param cards * @param source @@ -513,7 +511,6 @@ public interface Player extends MageItem, Copyable { * @param fromZone if null, this info isn't postet * @return */ - boolean moveCardsToGraveyardWithInfo(Cards cards, Ability source, Game game, Zone fromZone); boolean moveCardsToGraveyardWithInfo(List cards, Ability source, Game game, Zone fromZone); /** diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index e24075d5b12..8701815305c 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -113,6 +113,7 @@ import mage.game.events.DamagePlayerEvent; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeGroupEvent; import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -2844,14 +2845,19 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(List cards, Zone fromZone, Zone toZone, Ability source, Game game) { + if (cards.isEmpty()) { + return true; + } + game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone)); switch(toZone) { case GRAVEYARD: return moveCardsToGraveyardWithInfo(cards, source, game, fromZone); case HAND: + boolean result = false; for(Card card: cards) { - moveCardToHandWithInfo(card, playerId, game, fromZone); + result |= moveCardToHandWithInfo(card, playerId, game, fromZone); } - return true; + return result; default: throw new UnsupportedOperationException("to Zone not supported yet"); } @@ -2886,33 +2892,25 @@ public abstract class PlayerImpl implements Player, Serializable { } return result; } - - @Override - public boolean moveCardsToGraveyardWithInfo(Cards allCards, Ability source, Game game, Zone fromZone) { - ArrayList cards = new ArrayList<>(); - cards.addAll(allCards.getCards(game)); - return moveCardsToGraveyardWithInfo(cards, source, game, fromZone); - } - + @Override public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { while (!allCards.isEmpty()) { // identify cards from one owner Cards cards = new CardsImpl(); UUID ownerId = null; - for (Card card: allCards) { + for (Iterator it = allCards.iterator(); it.hasNext();) { + Card card = it.next(); if (cards.isEmpty()) { ownerId = card.getOwnerId(); } if (card.getOwnerId().equals(ownerId)) { + it.remove(); cards.add(card); } } - allCards.removeAll(cards.getCards(game)); // move cards ot graveyard in order the owner decides - if (cards.size() != 0) { - TargetCard target = new TargetCard(fromZone, new FilterCard("card to put on the top of your graveyard (last one chosen will be topmost)")); - target.setRequired(true); + if (!cards.isEmpty()) { Player choosingPlayer = this; if (ownerId != this.getId()) { choosingPlayer = game.getPlayer(ownerId); @@ -2925,6 +2923,8 @@ public abstract class PlayerImpl implements Player, Serializable { chooseOrder = choosingPlayer.chooseUse(Outcome.Neutral, "Would you like to choose the order the cards go to graveyard?", game); } if (chooseOrder) { + TargetCard target = new TargetCard(fromZone, new FilterCard("card to put on the top of your graveyard (last one chosen will be topmost)")); + target.setRequired(true); while (choosingPlayer.isInGame() && cards.size() > 1) { choosingPlayer.chooseTarget(Outcome.Neutral, cards, target, source, game); UUID targetObjectId = target.getFirstTarget(); @@ -2936,7 +2936,7 @@ public abstract class PlayerImpl implements Player, Serializable { target.clearChosen(); } if (cards.size() == 1) { - choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), source.getSourceId(), game, fromZone); + choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), source == null ? null : source.getSourceId(), game, fromZone); } } else { for (Card card : cards.getCards(game)) { diff --git a/Mage/src/mage/target/common/TargetPermanentOrPlayer.java b/Mage/src/mage/target/common/TargetPermanentOrPlayer.java index d43b5650908..9fddd067f79 100644 --- a/Mage/src/mage/target/common/TargetPermanentOrPlayer.java +++ b/Mage/src/mage/target/common/TargetPermanentOrPlayer.java @@ -83,7 +83,7 @@ public class TargetPermanentOrPlayer extends TargetImpl { @Override public Filter getFilter() { - return this.filter; + return filter; } public void setFilter(FilterPermanentOrPlayer filter) { diff --git a/Mage/src/mage/target/common/TargetPermanentOrPlayerWithCounter.java b/Mage/src/mage/target/common/TargetPermanentOrPlayerWithCounter.java index 463df692308..700c5c4f845 100644 --- a/Mage/src/mage/target/common/TargetPermanentOrPlayerWithCounter.java +++ b/Mage/src/mage/target/common/TargetPermanentOrPlayerWithCounter.java @@ -34,7 +34,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import java.util.UUID; -import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.CounterPredicate; From a5820a50e623ac34aeb9bd7a74f39ab8b06e8c7a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 16:34:32 +0200 Subject: [PATCH 03/12] Added tests for Auraswap. --- .../mage/sets/futuresight/ArcanumWings.java | 1 - .../modernmasters2015/WorldheartPhoenix.java | 3 +- .../riseoftheeldrazi/EldraziConscription.java | 16 ++- .../abilities/keywords/AuraSwapTest.java | 106 ++++++++++++++++++ 4 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AuraSwapTest.java diff --git a/Mage.Sets/src/mage/sets/futuresight/ArcanumWings.java b/Mage.Sets/src/mage/sets/futuresight/ArcanumWings.java index 32571d38f5c..6813e110d62 100644 --- a/Mage.Sets/src/mage/sets/futuresight/ArcanumWings.java +++ b/Mage.Sets/src/mage/sets/futuresight/ArcanumWings.java @@ -56,7 +56,6 @@ public class ArcanumWings extends CardImpl { this.expansionSetCode = "FUT"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); diff --git a/Mage.Sets/src/mage/sets/modernmasters2015/WorldheartPhoenix.java b/Mage.Sets/src/mage/sets/modernmasters2015/WorldheartPhoenix.java index 29ffc3e64a7..b5053cf16b1 100644 --- a/Mage.Sets/src/mage/sets/modernmasters2015/WorldheartPhoenix.java +++ b/Mage.Sets/src/mage/sets/modernmasters2015/WorldheartPhoenix.java @@ -31,11 +31,9 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; @@ -112,6 +110,7 @@ public class WorldheartPhoenix extends CardImpl { if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { Player player = game.getPlayer(affectedControllerId); if (player != null) { + // can sometimes be cast with base mana cost from grave???? player.setCastSourceIdWithAlternateMana(sourceId, new ManaCostsImpl<>("{W}{U}{B}{R}{G}")); return true; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziConscription.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziConscription.java index 118e1a529c6..7f0e7ac40b9 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziConscription.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziConscription.java @@ -36,6 +36,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -60,15 +61,22 @@ public class EldraziConscription extends CardImpl { this.subtype.add("Eldrazi"); this.subtype.add("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); - - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(10, 10, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(new AnnihilatorAbility(2), AttachmentType.AURA))); + + // Enchanted creature gets +10/+10 and has trample and annihilator 2 + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(10, 10, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA); + effect.setText("and has trample"); + ability.addEffect(effect); + effect = new GainAbilityAttachedEffect(new AnnihilatorAbility(2), AttachmentType.AURA); + effect.setText("and annihilator 2. (Whenever it attacks, defending player sacrifices two permanents.)"); + ability.addEffect(effect); + this.addAbility(ability); } public EldraziConscription (final EldraziConscription card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AuraSwapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AuraSwapTest.java new file mode 100644 index 00000000000..04b4ee733d1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AuraSwapTest.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 org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * 702.64. Aura swap + * 702.64a Aura swap is an activated ability of some Aura cards. + * "Aura swap [cost]" means "[Cost]: You may exchange this permanent with an Aura card in your hand." + * 702.64b If either half of the exchange can't be completed, the ability has no effect. + * + * @author LevelX2 + */ + +public class AuraSwapTest extends CardTestPlayerBase { + + /** + * Test normal swap + */ + @Test + public void testAuraSwap1() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // Enchanted creature has flying. + // Aura swap {2}{U} + addCard(Zone.HAND, playerA, "Arcanum Wings"); // {1}{U} + + // Enchant creature + // Enchanted creature gets +10/+10 and has trample and annihilator 2. (Whenever it attacks, defending player sacrifices two permanents.) + addCard(Zone.HAND, playerA, "Eldrazi Conscription"); // {8} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcanum Wings", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Aura swap"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, "Arcanum Wings", 1); + assertPermanentCount(playerA, "Eldrazi Conscription", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 12, 12); + } + /** + * Test swap canceled because enchantment disenchanted + */ + @Test + public void testAuraSwap2() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // Enchanted creature has flying. + // Aura swap {2}{U} + addCard(Zone.HAND, playerA, "Arcanum Wings"); // {1}{U} + + // Enchant creature + // Enchanted creature gets +10/+10 and has trample and annihilator 2. (Whenever it attacks, defending player sacrifices two permanents.) + addCard(Zone.HAND, playerA, "Eldrazi Conscription"); // {8} + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Disenchant"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcanum Wings", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Aura swap"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Disenchant", "Arcanum Wings", "Aura swap"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Disenchant", 1); + + assertGraveyardCount(playerA, "Arcanum Wings", 1); + assertPermanentCount(playerA, "Eldrazi Conscription", 0); + assertHandCount(playerA, "Eldrazi Conscription", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + } +} From 50e8e3930c86f5a557c92e4fb283a7add57d72be Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 16:49:16 +0200 Subject: [PATCH 04/12] * Vengeful Rebirth - Fixed target handling if second target chnaged zone until Vengeful Rebirth resolves. --- .../sets/alarareborn/VengefulRebirth.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java b/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java index 5ff5d755a68..a48753dd86d 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java @@ -41,7 +41,6 @@ import mage.cards.CardImpl; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreatureOrPlayer; @@ -55,12 +54,11 @@ public class VengefulRebirth extends CardImpl { super(ownerId, 62, "Vengeful Rebirth", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{4}{R}{G}"); this.expansionSetCode = "ARB"; - // Return target card from your graveyard to your hand. If you return a nonland card to your hand this way, {this} deals damage equal to that card's converted mana cost to target creature or player - Target target = new TargetCardInYourGraveyard(); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new VengefulRebirthEffect()); + // Exile Vengeful Rebirth. this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); } @@ -73,7 +71,6 @@ public class VengefulRebirth extends CardImpl { public VengefulRebirth copy() { return new VengefulRebirth(this); } - } class VengefulRebirthEffect extends OneShotEffect { @@ -94,17 +91,17 @@ class VengefulRebirthEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); Card card = (Card)game.getObject(source.getFirstTarget()); - if (player != null && card != null && player.removeFromGraveyard(card, game)) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + if (controller != null && card != null && controller.removeFromGraveyard(card, game)) { + controller.moveCards(card, Zone.GRAVEYARD, Zone.HAND, source, game); if (!card.getCardType().contains(CardType.LAND)) { - int damage = card.getManaCost().convertedManaCost(); - Permanent permanent = game.getPermanent(source.getTargets().get(1).getTargets().get(0)); + int damage = card.getManaCost().convertedManaCost(); + Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.damage(damage, source.getSourceId(), game, false, true); } - Player targetPlayer = game.getPlayer(source.getTargets().get(1).getTargets().get(0)); + Player targetPlayer = game.getPlayer(source.getTargets().get(1).getFirstTarget()); if (targetPlayer != null) { targetPlayer.damage(damage, source.getSourceId(), game, false, true); } From d427e9a315a2ab69e5e0784eb84660abaa73a44c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 16:59:53 +0200 Subject: [PATCH 05/12] * Hindering Light - Fixed that target check did not work correctly for modal spells (e.g. Cryptic Command). --- .../sets/shardsofalara/HinderingLight.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java b/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java index d0866344a9f..cd0dbc8e7f7 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java @@ -84,17 +84,19 @@ class HinderingLightPredicate implements ObjectPlayerPredicate Date: Sat, 23 May 2015 17:08:18 +0200 Subject: [PATCH 06/12] * Ghastlord of Fugue - Fixed the not working triggered ability. --- Mage.Sets/src/mage/sets/shadowmoor/GhastlordOfFugue.java | 4 ++-- Mage/src/mage/players/PlayerImpl.java | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GhastlordOfFugue.java b/Mage.Sets/src/mage/sets/shadowmoor/GhastlordOfFugue.java index 1e03e6627ef..ba11dbcf7e8 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GhastlordOfFugue.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GhastlordOfFugue.java @@ -93,7 +93,7 @@ class GhastlordOfFugueEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (targetPlayer != null @@ -111,7 +111,7 @@ class GhastlordOfFugueEffect extends OneShotEffect { chosenCard = game.getCard(target.getFirstTarget()); } if (chosenCard != null) { - controller.moveCardToExileWithInfo(chosenCard, null, "", source.getSourceId(), game, Zone.HAND, true); + controller.moveCards(chosenCard, Zone.HAND, Zone.EXILED, source, game); } return true; } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 8701815305c..208179d0c93 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2850,12 +2850,18 @@ public abstract class PlayerImpl implements Player, Serializable { } game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone)); switch(toZone) { + case EXILED: + boolean result = false; + for(Card card: cards) { + result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, true); + } + return result; case GRAVEYARD: return moveCardsToGraveyardWithInfo(cards, source, game, fromZone); case HAND: boolean result = false; for(Card card: cards) { - result |= moveCardToHandWithInfo(card, playerId, game, fromZone); + result |= moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone); } return result; default: From 7003dc7e3fcc06d543d51c69875e6cb69f58349b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 17:43:27 +0200 Subject: [PATCH 07/12] * Fixed that the AI used activated abilities even if they were forbidden by a restriction effect like Arrest. --- Mage.Sets/src/mage/sets/mirrodin/Arrest.java | 1 - .../test/cards/restriction/ArrestTest.java | 67 +++++++++++++++++++ Mage/src/mage/players/PlayerImpl.java | 11 ++- 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/restriction/ArrestTest.java diff --git a/Mage.Sets/src/mage/sets/mirrodin/Arrest.java b/Mage.Sets/src/mage/sets/mirrodin/Arrest.java index 01d4204336c..aaf206d24f5 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/Arrest.java +++ b/Mage.Sets/src/mage/sets/mirrodin/Arrest.java @@ -39,7 +39,6 @@ import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.combat.CantBlockAttackActivateAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; -import mage.constants.Duration; import mage.constants.Outcome; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/ArrestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/ArrestTest.java new file mode 100644 index 00000000000..cd6671ee25e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/ArrestTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.restriction; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ArrestTest extends CardTestPlayerBase { + + + @Test + public void testArrest1() { + addCard(Zone.HAND, playerA, "Arrest"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + addCard(Zone.BATTLEFIELD, playerB, "Forest", 4); + // {3}{G}: Put a 1/1 green Saproling creature token onto the battlefield. + // {3}{W}: Creatures you control get +1/+1 until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Selesnya Guildmage"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Arrest", "Selesnya Guildmage"); + attack(2, playerB, "Selesnya Guildmage"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Arrest", 1); + assertPermanentCount(playerB, "Saproling", 0); // can't use ability so no Saproling + + assertLife(playerA, 20); // can't attack so no damage to player + assertLife(playerB, 20); + + } + +} diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 208179d0c93..20a8e6cd8aa 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2442,11 +2442,10 @@ public abstract class PlayerImpl implements Player, Serializable { // eliminate duplicate activated abilities Map playableActivated = new HashMap<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { - for (ActivatedAbility ability : permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { + LinkedHashMap useableAbilities = getUseableActivatedAbilities(permanent, Zone.BATTLEFIELD, game); + for (ActivatedAbility ability : useableAbilities.values()) { if (!playableActivated.containsKey(ability.toString())) { - if (canPlay(ability, availableMana, permanent, game)) { - playableActivated.put(ability.toString(), ability); - } + playableActivated.put(ability.toString(), ability); } } } @@ -2853,13 +2852,13 @@ public abstract class PlayerImpl implements Player, Serializable { case EXILED: boolean result = false; for(Card card: cards) { - result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, true); + result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, true); } return result; case GRAVEYARD: return moveCardsToGraveyardWithInfo(cards, source, game, fromZone); case HAND: - boolean result = false; + result = false; for(Card card: cards) { result |= moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone); } From f4718deae4131596b54b4f82fe0b73f2da210aec Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 18:02:37 +0200 Subject: [PATCH 08/12] * Jareth, Leonine Titan - Fixed that the protection giving ability did not work correctly. --- .../KamiOfThePaintedRoad.java | 71 +----------- .../sets/dragonsoftarkir/PristineSkywise.java | 70 +----------- .../mage/sets/gatecrash/CartelAristocrat.java | 51 +-------- .../mage/sets/odyssey/ResilientWanderer.java | 49 +------- .../sets/onslaught/JarethLeonineTitan.java | 58 +--------- .../GainProtectionFromColorSourceEffect.java | 105 ++++++++++++++++++ 6 files changed, 116 insertions(+), 288 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorSourceEffect.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KamiOfThePaintedRoad.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KamiOfThePaintedRoad.java index 7aa6df3c5c4..5467155e3ed 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KamiOfThePaintedRoad.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KamiOfThePaintedRoad.java @@ -29,23 +29,13 @@ package mage.sets.championsofkamigawa; import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.filter.FilterCard; import mage.filter.common.FilterSpiritOrArcaneCard; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -77,62 +67,3 @@ public class KamiOfThePaintedRoad extends CardImpl { return new KamiOfThePaintedRoad(this); } } - -class GainProtectionFromColorSourceEffect extends GainAbilitySourceEffect { - - FilterCard protectionFilter; - - public GainProtectionFromColorSourceEffect(Duration duration) { - super(new ProtectionAbility(new FilterCard()), duration); - protectionFilter = (FilterCard)((ProtectionAbility)ability).getFilter(); - } - - public GainProtectionFromColorSourceEffect(final GainProtectionFromColorSourceEffect effect) { - super(effect); - this.protectionFilter = effect.protectionFilter.copy(); - } - - @Override - public GainProtectionFromColorSourceEffect copy() { - return new GainProtectionFromColorSourceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ChoiceColor colorChoice = new ChoiceColor(true); - colorChoice.setMessage("Choose color for protection ability"); - while (!colorChoice.isChosen()) { - controller.choose(outcome, colorChoice, game); - if (!controller.isInGame()) { - discard(); - return; - } - } - protectionFilter.add(new ColorPredicate(colorChoice.getColor())); - protectionFilter.setMessage(colorChoice.getChoice()); - ((ProtectionAbility)ability).setFilter(protectionFilter); - return; - } - discard(); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && new MageObjectReference(permanent, game).refersTo(source.getSourceObject(game), game)) { - permanent.addAbility(ability, source.getSourceId(), game); - } else { - // the source permanent is no longer on the battlefield, effect can be discarded - discard(); - } - return true; - } - - @Override - public String getText(Mode mode) { - return "{this} gains protection from the color of your choice " + duration.toString(); - } -} diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/PristineSkywise.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/PristineSkywise.java index aa892dcf11e..fb037d67178 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/PristineSkywise.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/PristineSkywise.java @@ -29,27 +29,18 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.UntapSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -88,62 +79,3 @@ public class PristineSkywise extends CardImpl { return new PristineSkywise(this); } } - -class GainProtectionFromColorSourceEffect extends GainAbilitySourceEffect { - - FilterCard protectionFilter; - - public GainProtectionFromColorSourceEffect(Duration duration) { - super(new ProtectionAbility(new FilterCard()), duration); - protectionFilter = (FilterCard)((ProtectionAbility)ability).getFilter(); - } - - public GainProtectionFromColorSourceEffect(final GainProtectionFromColorSourceEffect effect) { - super(effect); - this.protectionFilter = effect.protectionFilter.copy(); - } - - @Override - public GainProtectionFromColorSourceEffect copy() { - return new GainProtectionFromColorSourceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ChoiceColor colorChoice = new ChoiceColor(true); - colorChoice.setMessage("Choose color for protection ability"); - while (!colorChoice.isChosen()) { - controller.choose(outcome, colorChoice, game); - if (!controller.isInGame()) { - discard(); - return; - } - } - protectionFilter.add(new ColorPredicate(colorChoice.getColor())); - protectionFilter.setMessage(colorChoice.getChoice()); - ((ProtectionAbility)ability).setFilter(protectionFilter); - return; - } - discard(); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && new MageObjectReference(permanent, game).refersTo(source.getSourceObject(game), game)) { - permanent.addAbility(ability, source.getSourceId(), game); - } else { - // the source permanent is no longer on the battlefield, effect can be discarded - discard(); - } - return true; - } - - @Override - public String getText(Mode mode) { - return "{this} gains protection from the color of your choice " + duration.toString(); - } -} diff --git a/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java b/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java index fea5663b5eb..83086c0d761 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java +++ b/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java @@ -29,27 +29,18 @@ package mage.sets.gatecrash; import java.util.UUID; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; -import mage.filter.FilterCard; +import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetControlledPermanent; /** @@ -65,6 +56,7 @@ public class CartelAristocrat extends CardImpl { static { filter.add(new AnotherPredicate()); } + public CartelAristocrat(UUID ownerId) { super(ownerId, 150, "Cartel Aristocrat", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{W}{B}"); this.expansionSetCode = "GTC"; @@ -75,9 +67,8 @@ public class CartelAristocrat extends CardImpl { this.toughness = new MageInt(2); // Sacrifice another creature: Cartel Aristocrat gains protection from the color of your choice until end of turn. - Ability ability = new SimpleActivatedAbility( - Zone.BATTLEFIELD, new CartelAristocratEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter))); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility( + Zone.BATTLEFIELD, new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); } public CartelAristocrat(final CartelAristocrat card) { @@ -89,35 +80,3 @@ public class CartelAristocrat extends CardImpl { return new CartelAristocrat(this); } } -class CartelAristocratEffect extends OneShotEffect { - - public CartelAristocratEffect() { - super(Outcome.Protect); - this.staticText = "{this} gains protection from the color of your choice until end of turn"; - } - - public CartelAristocratEffect(final CartelAristocratEffect effect) { - super(effect); - } - - @Override - public CartelAristocratEffect copy() { - return new CartelAristocratEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - ChoiceColor choice = new ChoiceColor(); - choice.setMessage("Choose color to get protection from"); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && controller.choose(outcome, choice, game)) { - FilterCard protectionFilter = new FilterCard(); - protectionFilter.add(new ColorPredicate(choice.getColor())); - protectionFilter.setMessage(choice.getChoice().toLowerCase()); - ContinuousEffect effect = new GainAbilitySourceEffect(new ProtectionAbility(protectionFilter), Duration.EndOfTurn); - game.addEffect(effect, source); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/odyssey/ResilientWanderer.java b/Mage.Sets/src/mage/sets/odyssey/ResilientWanderer.java index 54c5dcc9451..15325016dff 100644 --- a/Mage.Sets/src/mage/sets/odyssey/ResilientWanderer.java +++ b/Mage.Sets/src/mage/sets/odyssey/ResilientWanderer.java @@ -30,23 +30,15 @@ package mage.sets.odyssey; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -66,7 +58,7 @@ public class ResilientWanderer extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); // Discard a card: Resilient Wanderer gains protection from the color of your choice until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainProtectionFromColorSourceEffect(), new DiscardCardCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), new DiscardCardCost()); this.addAbility(ability); } @@ -80,40 +72,3 @@ public class ResilientWanderer extends CardImpl { } } -class GainProtectionFromColorSourceEffect extends ContinuousEffectImpl { - - protected FilterCard protectionFilter; - - public GainProtectionFromColorSourceEffect() { - super(Duration.EndOfTurn, Outcome.AddAbility); - } - - public GainProtectionFromColorSourceEffect(final GainProtectionFromColorSourceEffect effect) { - super(effect); - } - - @Override - public GainProtectionFromColorSourceEffect copy() { - return new GainProtectionFromColorSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanent(source.getSourceId()); - if (creature != null) { - ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); - protectionFilter.add(new ColorPredicate(choice.getColor())); - protectionFilter.setMessage(choice.getChoice()); - ProtectionAbility ability = new ProtectionAbility(protectionFilter); - creature.addAbility(ability, source.getSourceId(), game); - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - return "{this} gains protection from the color of your choice " + duration.toString(); - } -} - diff --git a/Mage.Sets/src/mage/sets/onslaught/JarethLeonineTitan.java b/Mage.Sets/src/mage/sets/onslaught/JarethLeonineTitan.java index fda2d733ec2..d5f5fb4e51c 100644 --- a/Mage.Sets/src/mage/sets/onslaught/JarethLeonineTitan.java +++ b/Mage.Sets/src/mage/sets/onslaught/JarethLeonineTitan.java @@ -30,25 +30,16 @@ package mage.sets.onslaught; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.BlocksTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.effects.common.continuous.GainProtectionFromColorSourceEffect; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -69,7 +60,7 @@ public class JarethLeonineTitan extends CardImpl { // Whenever Jareth, Leonine Titan blocks, it gets +7/+7 until end of turn. this.addAbility(new BlocksTriggeredAbility(new BoostSourceEffect(7,7,Duration.EndOfTurn), false)); // {W}: Jareth gains protection from the color of your choice until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new JarethsGainProtectionFromColorSourceEffect(), new ManaCostsImpl("{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), new ManaCostsImpl("{W}")); this.addAbility(ability); } @@ -83,48 +74,3 @@ public class JarethLeonineTitan extends CardImpl { return new JarethLeonineTitan(this); } } - -class JarethsGainProtectionFromColorSourceEffect extends ContinuousEffectImpl { - - protected FilterCard protectionFilter; - - public JarethsGainProtectionFromColorSourceEffect() { - super(Duration.EndOfTurn, Outcome.AddAbility); - } - - public JarethsGainProtectionFromColorSourceEffect(final JarethsGainProtectionFromColorSourceEffect effect) { - super(effect); - } - - @Override - public JarethsGainProtectionFromColorSourceEffect copy() { - return new JarethsGainProtectionFromColorSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent creature = game.getPermanent(source.getSourceId()); - if (controller != null && creature != null) { - ChoiceColor choice = new ChoiceColor(); - while (!choice.isChosen()) { - controller.choose(Outcome.Protect, choice, game); - if (!controller.isInGame()) { - return false; - } - } - protectionFilter.add(new ColorPredicate(choice.getColor())); - protectionFilter.setMessage(choice.getChoice()); - ProtectionAbility ability = new ProtectionAbility(protectionFilter); - creature.addAbility(ability, source.getSourceId(), game); - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - return "{this} gains protection from the color of your choice " + duration.toString(); - } -} - diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorSourceEffect.java new file mode 100644 index 00000000000..656c7b23fce --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorSourceEffect.java @@ -0,0 +1,105 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common.continuous; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.keyword.ProtectionAbility; +import mage.choices.ChoiceColor; +import mage.constants.Duration; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ + +public class GainProtectionFromColorSourceEffect extends GainAbilitySourceEffect { + + FilterCard protectionFilter; + + public GainProtectionFromColorSourceEffect(Duration duration) { + super(new ProtectionAbility(new FilterCard()), duration); + protectionFilter = (FilterCard)((ProtectionAbility)ability).getFilter(); + } + + public GainProtectionFromColorSourceEffect(final GainProtectionFromColorSourceEffect effect) { + super(effect); + this.protectionFilter = effect.protectionFilter.copy(); + } + + @Override + public GainProtectionFromColorSourceEffect copy() { + return new GainProtectionFromColorSourceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ChoiceColor colorChoice = new ChoiceColor(true); + colorChoice.setMessage("Choose color for protection ability"); + while (!colorChoice.isChosen()) { + controller.choose(outcome, colorChoice, game); + if (!controller.isInGame()) { + discard(); + return; + } + } + game.informPlayers("Choosen color: " + colorChoice.getColor()); + protectionFilter.add(new ColorPredicate(colorChoice.getColor())); + protectionFilter.setMessage(colorChoice.getChoice()); + ((ProtectionAbility)ability).setFilter(protectionFilter); + return; + } + discard(); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && new MageObjectReference(permanent, game).refersTo(source.getSourceObject(game), game)) { + permanent.addAbility(ability, source.getSourceId(), game); + } else { + // the source permanent is no longer on the battlefield, effect can be discarded + discard(); + } + return true; + } + + @Override + public String getText(Mode mode) { + return "{this} gains protection from the color of your choice " + duration.toString(); + } +} \ No newline at end of file From e82ee26136018f6da2be4d98b1dd43bb9f281370 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 18:38:34 +0200 Subject: [PATCH 09/12] Added test for Sepulchral Primordial. --- .../sets/gatecrash/DiluvianPrimordial.java | 36 +++++----- .../sets/gatecrash/SepulchralPrimordial.java | 17 +++-- .../mage/test/multiplayer/PrimordialTest.java | 70 +++++++++++++++++++ 3 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java diff --git a/Mage.Sets/src/mage/sets/gatecrash/DiluvianPrimordial.java b/Mage.Sets/src/mage/sets/gatecrash/DiluvianPrimordial.java index ef979340321..634afc77153 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/DiluvianPrimordial.java +++ b/Mage.Sets/src/mage/sets/gatecrash/DiluvianPrimordial.java @@ -122,20 +122,25 @@ class DiluvianPrimordialEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - for (Target target: source.getTargets()) { - if (target instanceof TargetCardInOpponentsGraveyard) { - Card targetCard = game.getCard(target.getFirstTarget()); - if (controller != null && targetCard != null) { - if (controller.chooseUse(outcome, "Cast " + targetCard.getName() +"?", game)) { - controller.cast(targetCard.getSpellAbility(), game, true); - ContinuousEffect effect = new DiluvianPrimordialReplacementEffect(); - effect.setTargetPointer(new FixedTarget(targetCard.getId())); - game.addEffect(effect, source); + if (controller != null) { + for (Target target: source.getTargets()) { + if (target instanceof TargetCardInOpponentsGraveyard) { + Card targetCard = game.getCard(target.getFirstTarget()); + if (targetCard != null) { + if (controller.chooseUse(outcome, "Cast " + targetCard.getLogName() +"?", game)) { + // TODO: Handle the case if the cast is not possible, so the replacement effect shouldn't be active + ContinuousEffect effect = new DiluvianPrimordialReplacementEffect(); + effect.setTargetPointer(new FixedTarget(targetCard.getId())); + game.addEffect(effect, source); + + controller.cast(targetCard.getSpellAbility(), game, true); + } } } } + return true; } - return true; + return false; } } @@ -164,9 +169,9 @@ class DiluvianPrimordialReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); + Card card = game.getCard(((FixedTarget)getTargetPointer()).getTarget()); if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true); + controller.moveCards(card, Zone.STACK, Zone.EXILED, source, game); return true; } } @@ -181,10 +186,7 @@ class DiluvianPrimordialReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getToZone() == Zone.GRAVEYARD - && ((ZoneChangeEvent) event).getTargetId().equals(getTargetPointer().getFirst(game, source))) { - return true; - } - return false; + return zEvent.getToZone() == Zone.GRAVEYARD + && ((ZoneChangeEvent) event).getTargetId().equals(getTargetPointer().getFirst(game, source)); } } diff --git a/Mage.Sets/src/mage/sets/gatecrash/SepulchralPrimordial.java b/Mage.Sets/src/mage/sets/gatecrash/SepulchralPrimordial.java index 80783a03693..107a510ec4f 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/SepulchralPrimordial.java +++ b/Mage.Sets/src/mage/sets/gatecrash/SepulchralPrimordial.java @@ -113,15 +113,18 @@ class SepulchralPrimordialEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - for (Target target: source.getTargets()) { - if (target instanceof TargetCardInOpponentsGraveyard) { - Card targetCard = game.getCard(target.getFirstTarget()); - if (player != null && targetCard != null) { - targetCard.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (Target target: source.getTargets()) { + if (target instanceof TargetCardInOpponentsGraveyard) { + Card targetCard = game.getCard(target.getFirstTarget()); + if (targetCard != null) { + targetCard.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + } } } + return true; } - return true; + return false; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java new file mode 100644 index 00000000000..6cd03426854 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.multiplayer; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class PrimordialTest extends CardTestMultiPlayerBase { + + /** + * Tests Primordial cards with multiplayer effects + * + */ + @Test + public void SepulchralPrimordialTest() { + // When Sepulchral Primordial enters the battlefield, for each opponent, you may put up to one + // target creature card from that player's graveyard onto the battlefield under your control. + addCard(Zone.HAND, playerA, "Sepulchral Primordial"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",7); + + addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerC, "Walking Corpse"); + addCard(Zone.GRAVEYARD, playerD, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sepulchral Primordial"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Sepulchral Primordial", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Walking Corpse", 0); + assertPermanentCount(playerA, "Pillarfield Ox", 1); + assertGraveyardCount(playerC, "Walking Corpse", 1); + } + +} \ No newline at end of file From 28d816b21faad50aa5bdf6d32e5882074c82b6ec Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 18:59:41 +0200 Subject: [PATCH 10/12] * Endrek Sahr, Master Breeder - Fixed Thrull creating ability that did not work when the triggering spell was countered. --- .../EndrekSahrMasterBreeder.java | 4 ++ .../EndrekSahrMasterBreederTest.java | 72 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/EndrekSahrMasterBreederTest.java diff --git a/Mage.Sets/src/mage/sets/commander2013/EndrekSahrMasterBreeder.java b/Mage.Sets/src/mage/sets/commander2013/EndrekSahrMasterBreeder.java index 30166e0aeee..9396551ffe3 100644 --- a/Mage.Sets/src/mage/sets/commander2013/EndrekSahrMasterBreeder.java +++ b/Mage.Sets/src/mage/sets/commander2013/EndrekSahrMasterBreeder.java @@ -48,6 +48,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.token.Token; import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; /** * @@ -136,6 +137,9 @@ class EndrekSahrMasterBreederEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spell == null) { + spell = (Spell) game.getLastKnownInformation(((FixedTarget)getTargetPointer()).getTarget(), Zone.STACK); + } if (spell != null) { int cmc = spell.getConvertedManaCost(); if (cmc > 0) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/EndrekSahrMasterBreederTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/EndrekSahrMasterBreederTest.java new file mode 100644 index 00000000000..01fdf896f50 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/EndrekSahrMasterBreederTest.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 org.mage.test.cards.abilities.oneshot.counterspell; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class EndrekSahrMasterBreederTest extends CardTestPlayerBase { + + /** + * Test that tokens are created also if the spell that triggers Endreks ability is countered + * + */ + @Test + public void testTokenAlsoIfCountered() { + // Whenever you cast a creature spell, put X 1/1 black Thrull creature tokens onto the battlefield, where X is that spell's converted mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Endrek Sahr, Master Breeder", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Counterspell", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Silvercoat Lion", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Counterspell", 1); + + assertPermanentCount(playerA, "Thrull", 2); + + } + +} \ No newline at end of file From a183afc98777111b769f701630ccf075cf9eb3cc Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 19:21:45 +0200 Subject: [PATCH 11/12] * Drooling Groodion - Fixed that the same creature could be targeted from both targets of the activated ability. --- .../sets/jacevsvraska/DroolingGroodion.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java b/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java index 62079dee074..b27d9824f79 100644 --- a/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java +++ b/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java @@ -67,7 +67,7 @@ public class DroolingGroodion extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DroolingGroodionEffect(), new ManaCostsImpl("{2}{B}{G}")); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent(), true))); ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (first target)"))); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (second target)"))); + ability.addTarget(new TargetOtherCreaturePermanent(new FilterCreaturePermanent("creature (second target)"))); this.addAbility(ability); } @@ -112,3 +112,28 @@ class DroolingGroodionEffect extends ContinuousEffectImpl { return true; } } + +class TargetOtherCreaturePermanent extends TargetCreaturePermanent { + + public TargetOtherCreaturePermanent(FilterCreaturePermanent filter) { + super(filter); + } + + public TargetOtherCreaturePermanent(final TargetOtherCreaturePermanent target) { + super(target); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (source.getTargets().get(0).getTargets().contains(id)) { + return false; + } + return super.canTarget(controllerId, id, source, game); + } + + @Override + public TargetOtherCreaturePermanent copy() { + return new TargetOtherCreaturePermanent(this); + } + +} From abeac7ff6572c3bae51f5d688ed5f6ea0d9d8224 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 23 May 2015 19:38:30 +0200 Subject: [PATCH 12/12] Added another test for Laboratory Maniac. --- .../cards/replacement/WinLoseEffectsTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java index cf99cec6c9e..d7ee8020536 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java @@ -62,5 +62,35 @@ public class WinLoseEffectsTest extends CardTestPlayerBase { assertLife(playerB, 20); } + /** + * If I have resolved an Angel's Grace this turn, have an empty library, a Laboratory Maniac on + * the battlefield, and would draw a card, nothing happens. I should win the game if the card drawing effect resolves. + */ + @Test + public void testAngelsGrace() { + addCard(Zone.HAND, playerA, "Angel's Grace"); + // Prevent the next 1 damage that would be dealt to target creature or player this turn. + // Draw a card. + addCard(Zone.HAND, playerA, "Bandage"); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Laboratory Maniac", 1); + + skipInitShuffling(); + + playerA.getLibrary().clear(); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel's Grace"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Bandage"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + Assert.assertEquals("Player A library is empty", 0 , playerA.getLibrary().size()); + Assert.assertTrue("Player A has not won but should have", playerA.hasWon()); + assertLife(playerA, 20); + assertLife(playerB, 20); + + } }