From 32a123e799d20b819bee55fc6bd9333986329d4c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 12 Jun 2015 15:04:46 +0200 Subject: [PATCH 01/33] Added a test. --- .../cards/triggers/WorldgorgerDragonTest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java index e344f493414..c1f2238dfb1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java @@ -7,6 +7,7 @@ package org.mage.test.cards.triggers; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -116,4 +117,73 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { } + /** + * v9: Worldgorger Dragon + Animate Dead is still acting up (yey complex rules interactions!). + * The first time you return Animate Dead from Worldgorger's exile, it works like it's supposed + * to. You have to pick a creature, and it brings it back. But if you pick Worldgorger Dragon + * again, it allows you to not pick a creature, and regardless of whether you choose to skip or pick + * a different creature, it always returns the first creature you picked. Kind of hard to explain, + * but here's how to reproduce: + * + * 1) Cast Animate Dead, targeting Worldgorger Dragon + * 2) Worldgorger Dragon will exile Animate Dead, killing the dragon and returning the permanents + * 3) Select Worldgorger again + * 4) Step 2 repeats + * 5) Attempt to select a different creature. Worldgorger Dragon is returned instead. + * + */ + @Test + @Ignore + public void testWithAnimateDeadDifferentTargets() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // When Worldgorger Dragon enters the battlefield, exile all other permanents you control. + // When Worldgorger Dragon leaves the battlefield, return the exiled cards to the battlefield under their owners' control. + addCard(Zone.GRAVEYARD, playerA, "Worldgorger Dragon", 1); + + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 1); + // When Animate Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" + // and gains "enchant creature put onto the battlefield with Animate Dead." Return enchanted creature card to the battlefield + // under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it. + addCard(Zone.HAND, playerA, "Animate Dead"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Instant {X}{R}{R} + // Volcanic Geyser deals X damage to target creature or player. + addCard(Zone.HAND, playerA, "Volcanic Geyser", 1); + // When Staunch Defenders enters the battlefield, you gain 4 life. + addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon"); + addTarget(playerA, "Worldgorger Dragon"); + addTarget(playerA, "Worldgorger Dragon"); + addTarget(playerA, "Silvercoat Lion"); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 9); + setChoice(playerA, "X=7"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Worldgorger Dragon", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + assertLife(playerA, 24); + assertLife(playerB, 11); + + assertGraveyardCount(playerA, "Volcanic Geyser", 1); + + + } + } \ No newline at end of file From 05b654b5bdbbf98ccc190f9eb435815b3394a79e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 10:27:44 +0200 Subject: [PATCH 02/33] Proliferate - Added object id to the displayed permanent's name if the player has to choose which counter to proliferate. --- .../abilities/effects/common/counter/ProliferateEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/abilities/effects/common/counter/ProliferateEffect.java b/Mage/src/mage/abilities/effects/common/counter/ProliferateEffect.java index 363012453c2..b85e47bf840 100644 --- a/Mage/src/mage/abilities/effects/common/counter/ProliferateEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/ProliferateEffect.java @@ -87,7 +87,7 @@ public class ProliferateEffect extends OneShotEffect { choices.add(counter.getName()); } choice.setChoices(choices); - choice.setMessage("Choose a counter to proliferate (" + permanent.getName() + ")"); + choice.setMessage("Choose a counter to proliferate (" + permanent.getIdName() + ")"); controller.choose(Outcome.Benefit, choice, game); for (Counter counter : permanent.getCounters().values()) { if (counter.getName().equals(choice.getChoice())) { From 6e2441cc42bb0ae6e29e5e24ccb408976b13aae0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 14:52:22 +0200 Subject: [PATCH 03/33] * Apostle's Blessing - Fixed tooltip text. --- Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java index 79bdbcafa4f..1106bb635aa 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java @@ -87,7 +87,7 @@ class ApostlesBlessingEffect extends OneShotEffect { public ApostlesBlessingEffect() { super(Outcome.AddAbility); - this.staticText = "Target artifact or creature gains protection from artifacts or from the color of your choice until end of turn"; + this.staticText = "Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn"; } public ApostlesBlessingEffect(final ApostlesBlessingEffect effect) { From 0b2e61081a2f8b2dfa4d5b5ea0c85f02b289be29 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 14:53:23 +0200 Subject: [PATCH 04/33] Fixed a problem with the country flag resource name causing null pointer exceptions and graphical problems on the client side. --- .../src/main/java/mage/client/dialog/PreferencesDialog.java | 2 +- .../mage/client/util/gui/countryBox/CountryCellRenderer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 1a92477f2fd..c9d2f27d8fa 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -2422,7 +2422,7 @@ public class PreferencesDialog extends javax.swing.JDialog { PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true"), getUserSkipPrioritySteps(), - MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png"), + MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true").equals("true") ); } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryCellRenderer.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryCellRenderer.java index cc26e5bedca..2ee392fe9ea 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryCellRenderer.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryCellRenderer.java @@ -68,7 +68,7 @@ public class CountryCellRenderer extends DefaultTableCellRenderer { private ImageIcon getCountryFlagIcon(String countryCode) { ImageIcon flagIcon = flagIconCache.get(countryCode); if (flagIcon == null) { - flagIcon = new javax.swing.ImageIcon(getClass().getResource("/flags/" + countryCode +".png")); + flagIcon = new javax.swing.ImageIcon(getClass().getResource("/flags/" + countryCode + (countryCode.endsWith(".png") ? "" :".png"))); if (flagIcon.getImage() == null) { logger.warn("Country flag resource not found: " + countryCode); } else { From 725e7c5454b2bf473ee37c18ac9c0891622a2df2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 14:53:57 +0200 Subject: [PATCH 05/33] Update version to 1.4.1v1 to fix the graphical UI problems on the client. --- Mage.Common/src/mage/utils/MageVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index 79436cc949c..f30ec277dde 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -42,7 +42,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 1; - public final static String MAGE_VERSION_MINOR_PATCH = "v0"; + public final static String MAGE_VERSION_MINOR_PATCH = "v1"; public final static String MAGE_VERSION_INFO = ""; private final int major; From f6ef6f66673b797cb8fff6c4b2ff8cb07feefd8a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 15:00:51 +0200 Subject: [PATCH 06/33] Updated 1.4.1v1 version id. --- Utils/release/getting_implemented_cards.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index bf50704b7d0..203bc260828 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -134,6 +134,9 @@ git log eb96b08dfac3de4f78403d6f23e41ce8d41ece6f..HEAD --diff-filter=A --name-st since 1.4.1.v0 git log 7a54d5364c9789ce2c3ef2c3eb4df7e0e0cde0cf..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.1.v1 +git log 3e9b4cfb7c22d363755d28f5ff1de351f6b7123c..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt + 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: > perl extract_in_wiki_format.perl From 35654a5ca8520930bb58ad4837e2099a0f9cdbf0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 18:39:01 +0200 Subject: [PATCH 07/33] Fixed that by some effects that move permanents from battlefield to other zones, the object was not moved and was still on the battlefield but also shown in the target zone. --- Mage/src/mage/players/PlayerImpl.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index d0083372707..732fe47137e 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2839,7 +2839,19 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game) { ArrayList cardList = new ArrayList<>(); - cardList.addAll(cards.getCards(game)); + for (UUID cardId: cards) { + if (fromZone.equals(Zone.BATTLEFIELD)) { + Permanent permanent = game.getPermanent(cardId); + if (permanent != null) { + cardList.add(permanent); + } + } else { + Card card = game.getCard(cardId); + if (card != null) { + cardList.add(card); + } + } + } return moveCards(cardList, fromZone, toZone, source, game); } From 324188a86e6440b3bcba08c5d89eaeefe63cf2cb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 23:22:38 +0200 Subject: [PATCH 08/33] Fixed missing copy statements of cardAttribute in game state. --- Mage.Sets/src/mage/sets/gatecrash/Bioshift.java | 1 - Mage/src/mage/game/CardAttribute.java | 8 ++++++++ Mage/src/mage/game/GameState.java | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java b/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java index 27cb238c2fc..01764c518ab 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java +++ b/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java @@ -52,7 +52,6 @@ public class Bioshift extends CardImpl { super(ownerId, 214, "Bioshift", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G/U}"); this.expansionSetCode = "GTC"; - // Move any number of +1/+1 counters from target creature onto another target creature with the same controller. getSpellAbility().addEffect(new MoveCounterFromTargetToTargetEffect()); getSpellAbility().addTarget(new TargetCreaturePermanentSameController(2,2,new FilterCreaturePermanent(),false)); diff --git a/Mage/src/mage/game/CardAttribute.java b/Mage/src/mage/game/CardAttribute.java index 95b4659ab7d..49cbf8dce27 100644 --- a/Mage/src/mage/game/CardAttribute.java +++ b/Mage/src/mage/game/CardAttribute.java @@ -21,6 +21,14 @@ public class CardAttribute implements Serializable { public CardAttribute(Card card) { color = card.getColor(null).copy(); } + + public CardAttribute(CardAttribute cardAttribute) { + this.color = cardAttribute.color; + } + + public CardAttribute copy() { + return new CardAttribute(this); + } public ObjectColor getColor() { return color; diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index ebfdc555a19..83236ef4e69 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -171,6 +171,9 @@ public class GameState implements Serializable, Copyable { for (Map.Entry entry: state.cardState.entrySet()) { cardState.put(entry.getKey(), entry.getValue().copy()); } + for (Map.Entry entry: state.cardAttribute.entrySet()) { + cardAttribute.put(entry.getKey(), entry.getValue().copy()); + } this.zoneChangeCounter.putAll(state.zoneChangeCounter); this.copiedCards.putAll(state.copiedCards); this.permanentOrderNumber = state.permanentOrderNumber; @@ -208,6 +211,7 @@ public class GameState implements Serializable, Copyable { this.zones = state.zones; this.simultaneousEvents = state.simultaneousEvents; this.cardState = state.cardState; + this.cardAttribute = state.cardAttribute; this.zoneChangeCounter = state.zoneChangeCounter; this.copiedCards = state.copiedCards; this.permanentOrderNumber = state.permanentOrderNumber; From 135c76ed71bf64c9e0037462bfc862c7bfa1ac74 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 23:23:26 +0200 Subject: [PATCH 09/33] Added possibility to set a specific deck for a test. --- Mage.Tests/CommanderDuel_UW.dck | 70 +++++++++++++++++++ .../base/CardTestCommanderDuelBase.java | 10 ++- .../serverside/base/CardTestPlayerBase.java | 9 ++- .../base/impl/CardTestPlayerAPIImpl.java | 11 +++ 4 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/CommanderDuel_UW.dck diff --git a/Mage.Tests/CommanderDuel_UW.dck b/Mage.Tests/CommanderDuel_UW.dck new file mode 100644 index 00000000000..795b083a073 --- /dev/null +++ b/Mage.Tests/CommanderDuel_UW.dck @@ -0,0 +1,70 @@ +NAME:Geoff's Daxos of Meletis +1 [THS:58] Ordeal of Thassa +1 [5ED:388] Mana Vault +1 [THS:14] Gift of Immortality +1 [PC2:12] Spirit Mantle +1 [M13:36] Sublime Archangel +1 [M13:206] Gilded Lotus +1 [M11:5] Armored Ascension +1 [ALA:27] Sigiled Paladin +1 [ALA:26] Sighted-Caste Sorcerer +1 [CON:2] Asha's Favor +1 [M14:210] Fireshrieker +1 [GTC:17] Holy Mantle +1 [CHK:65] Hinder +1 [DGM:96] Render Silent +1 [M13:74] Tricks of the Trade +1 [ISD:229] Mask of Avacyn +1 [GPT:28] Infiltrator's Magemark +14 [THS:234] Island +1 [CMD:261] Sol Ring +1 [THS:25] Ordeal of Heliod +1 [5DN:131] Helm of Kaldra +1 [M12:24] Lifelink +1 [AVR:50] Elgaud Shieldmate +19 [THS:230] Plains +1 [M14:23] Indestructibility +1 [M14:5] Archangel of Thune +1 [CMD:265] Azorius Chancery +1 [ARB:3] Ethercaste Knight +1 [ARB:1] Ardent Plea +1 [M13:18] Guardians of Akrasa +1 [CMD:40] Brainstorm +1 [UDS:139] Thran Dynamo +1 [M12:219] Swiftfoot Boots +1 [THS:39] Aqueous Form +1 [M13:6] Aven Squire +1 [MMA:25] Path to Exile +1 [M13:4] Angelic Benediction +1 [CMD:253] Lightning Greaves +1 [SHM:150] Steel of the Godhead +1 [M13:225] Glacial Fortress +1 [10E:40] Serra's Embrace +1 [JUD:31] Unquestioned Authority +1 [M13:221] Cathedral of War +1 [RTR:9] Ethereal Armor +1 [DDL:27] Battle Mastery +1 [DDL:24] Bonds of Faith +1 [ISD:53] Dissipate +1 [DKA:148] Executioner's Hood +1 [DIS:159] Azorius Signet +1 [GTC:37] Hands of Binding +1 [PC2:112] Quietus Spike +1 [MRD:144] Banshee's Blade +1 [AVR:36] Silverblade Paladin +1 [PC2:115] Whispersilk Cloak +1 [M12:73] Ponder +1 [M13:29] Rhox Faithmender +1 [ALA:1] Akrasan Squire +1 [M13:213] Ring of Thune +1 [UDS:46] Sigil of Sleep +1 [M13:211] Ring of Evos Isle +1 [RTR:241] Hallowed Fountain +1 [7ED:67] Counterspell +1 [ISD:49] Curiosity +1 [SOM:229] Seachrome Coast +1 [PC2:10] Mammoth Umbra +1 [MBS:104] Darksteel Plate +1 [M13:21] Knight of Glory +1 [M13:22] Oblivion Ring +SB: 1 [THS:191] Daxos of Meletis diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java index c31449aa0e8..df244f549d7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java @@ -42,12 +42,18 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl { + public CardTestCommanderDuelBase() { + super(); + this.deckNameA = "CommanderDuel.dck"; + this.deckNameB = "CommanderDuel.dck"; + } + @Override protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 40); - playerA = createPlayer(game, playerA, "PlayerA","CommanderDuel.dck"); - playerB = createPlayer(game, playerB, "PlayerB","CommanderDuel.dck"); + playerA = createPlayer(game, playerA, "PlayerA",deckNameA); + playerB = createPlayer(game, playerB, "PlayerB",deckNameB); return game; } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java index e3435c3d3f4..5c92e590068 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java @@ -15,12 +15,17 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; */ public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { + public CardTestPlayerBase() { + deckNameA = "RB Aggro.dck"; + deckNameB = "RB Aggro.dck"; + } + @Override protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); - playerA = createPlayer(game, playerA, "PlayerA"); - playerB = createPlayer(game, playerB, "PlayerB"); + playerA = createPlayer(game, playerA, "PlayerA", deckNameA); + playerB = createPlayer(game, playerB, "PlayerB", deckNameB); return game; } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index dc059ca7cf6..8b910600cb8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -44,6 +44,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement protected GameOptions gameOptions; + protected String deckNameA; + protected String deckNameB; + protected enum ExpectedType { TURN_NUMBER, RESULT, @@ -1009,6 +1012,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement player.addTarget("targetPlayer="+targetPlayer.getName()); } + public void setDecknamePlayerA(String deckname) { + deckNameA = deckname; + } + + public void setDecknamePlayerB(String deckname) { + deckNameB = deckname; + } + protected void skipInitShuffling() { gameOptions.skipInitShuffling = true; } From 516b6ea8e777f8b3a4b03a9c929525594819f937 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 23:24:15 +0200 Subject: [PATCH 10/33] * Teferi, Mage of Zhalfir - Fixed that the flash ability was not applied to commander in command zone. --- .../sets/timespiral/TeferiMageOfZhalfir.java | 9 ++- .../duel/TeferiMageOfZhalfirTest.java | 70 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java diff --git a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java index 12ce53027ff..26395fa7c3a 100644 --- a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java +++ b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java @@ -131,7 +131,14 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl { if (card.getOwnerId().equals(controller.getId()) && card.getCardType().contains(CardType.CREATURE)) { game.getState().addOtherAbility(card, FlashAbility.getInstance()); } - } + } + // commander in command zone + if (controller.getCommanderId() != null && game.getState().getZone(controller.getCommanderId()).equals(Zone.COMMAND)) { + Card card = game.getCard(controller.getCommanderId()); + if (card.getCardType().contains(CardType.CREATURE)) { + game.getState().addOtherAbility(card, FlashAbility.getInstance()); + } + } return true; } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java new file mode 100644 index 00000000000..9171cb94a0b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.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.commander.duel; + + +import java.io.FileNotFoundException; +import mage.abilities.keyword.FlashAbility; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.GameException; +import mage.game.command.CommandObject; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ + +public class TeferiMageOfZhalfirTest extends CardTestCommanderDuelBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + setDecknamePlayerA("CommanderDuel_UW.dck"); // Commander = Daxos of Meletis + return super.createNewGameAndPlayers(); + } + + @Test + public void castCommanderWithFlash() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir"); + + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Daxos of Meletis"); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerA, "Daxos of Meletis", 1); + + } +} \ No newline at end of file From 75645946e0f5c88badd33327f8ad5e364c19f198 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Jun 2015 23:56:23 +0200 Subject: [PATCH 11/33] * Searing Blaze - Fixed that damage was not raised to 3 if the controller played a land before. --- .../src/mage/sets/worldwake/SearingBlaze.java | 26 ++++++-------- .../abilities/keywords/LandfallTest.java | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java index c5b2b73ac36..2cccea80098 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java +++ b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java @@ -96,24 +96,18 @@ class SearingBlazeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("LandPlayed", source.getControllerId()); + LandfallWatcher watcher = (LandfallWatcher) game.getState().getWatchers().get("LandPlayed"); Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget()); Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (watcher != null && watcher.conditionMet()) { - if (player != null) { - player.damage(3, source.getSourceId(), game, false, true); - } - if (creature != null) { - creature.damage(3, source.getSourceId(), game, false, true); - } + int damage = 1; + if (watcher != null && watcher.landPlayed(source.getControllerId())) { + damage = 3; } - else { - if (player != null) { - player.damage(1, source.getSourceId(), game, false, true); - } - if (creature != null) { - creature.damage(1, source.getSourceId(), game, false, true); - } + if (player != null) { + player.damage(damage, source.getSourceId(), game, false, true); + } + if (creature != null) { + creature.damage(damage, source.getSourceId(), game, false, true); } return true; } @@ -143,7 +137,7 @@ class SearingBlazeTarget extends TargetPermanent { @Override public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { Set availablePossibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); - Set possibleTargets = new HashSet(); + Set possibleTargets = new HashSet<>(); MageObject object = game.getObject(sourceId); if (object instanceof StackObject) { UUID playerId = ((StackObject)object).getStackAbility().getFirstTarget(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java index ef5349b9ae8..0f4ab1f663b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java @@ -111,5 +111,40 @@ public class LandfallTest extends CardTestPlayerBase { assertLife(playerB, 20); } + + /** + * Searing Blaze's landfall doesn't appear to be working. My opponent played + * a mountain, then played searing blaze targeting my Tasigur, the Golden + * Fang. It only dealt 1 damage to me, where it should've dealt 3, because + * my opponent had played a land. + */ + @Test + public void testSearingBlaze() { + // Searing Blaze deals 1 damage to target player and 1 damage to target creature that player controls. + // Landfall - If you had a land enter the battlefield under your control this turn, Searing Blaze deals 3 damage to that player and 3 damage to that creature instead. + addCard(Zone.HAND, playerA, "Searing Blaze",1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain",1); + addCard(Zone.HAND, playerA, "Mountain"); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion",1); + + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Searing Blaze"); + + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Mountain", 2); + assertGraveyardCount(playerA, "Searing Blaze" , 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + assertGraveyardCount(playerB, "Silvercoat Lion" , 1); + + } } From bd4a0e7177fc04f037617ba4b2ca9f66667251c7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 15 Jun 2015 07:44:34 +0200 Subject: [PATCH 12/33] * Added test for commander damage wind condition. --- .../commander/duel/CastCommanderTest.java | 4 +-- .../duel/TeferiMageOfZhalfirTest.java | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java index 33bbc816b54..6af9e08c961 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -38,7 +38,7 @@ import org.mage.test.serverside.base.CardTestCommanderDuelBase; */ public class CastCommanderTest extends CardTestCommanderDuelBase { @Test - public void testFirstAbility() { + public void testCastCommander() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); @@ -50,5 +50,5 @@ public class CastCommanderTest extends CardTestCommanderDuelBase { assertLife(playerB, 40); assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); - } + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java index 9171cb94a0b..08c03b60468 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/TeferiMageOfZhalfirTest.java @@ -29,13 +29,10 @@ package org.mage.test.commander.duel; import java.io.FileNotFoundException; -import mage.abilities.keyword.FlashAbility; -import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.game.Game; import mage.game.GameException; -import mage.game.command.CommandObject; import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommanderDuelBase; @@ -67,4 +64,35 @@ public class TeferiMageOfZhalfirTest extends CardTestCommanderDuelBase { assertPermanentCount(playerA, "Daxos of Meletis", 1); } + + @Test + public void testCommanderDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // Enchant creature + // Enchanted creature gets +4/+4, has flying and first strike, and is an Angel in addition to its other types. + // When enchanted creature dies, return Angelic Destiny to its owner's hand. + addCard(Zone.HAND, playerA, "Angelic Destiny"); + + addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir"); + + // Daxos of Meletis can't be blocked by creatures with power 3 or greater. + // Whenever Daxos of Meletis deals combat damage to a player, exile the top card of that player's library. You gain life equal to that card's converted mana cost. Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Daxos of Meletis"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angelic Destiny","Daxos of Meletis"); + + attack(3, playerA, "Daxos of Meletis"); + attack(5, playerA, "Daxos of Meletis"); + attack(7, playerA, "Daxos of Meletis"); + attack(9, playerA, "Daxos of Meletis"); + + setStopAt(9, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Daxos of Meletis", 1); + assertPowerToughness(playerA, "Daxos of Meletis", 6, 6); + + Assert.assertEquals("Player A has won because of commander damage", true, playerA.hasWon()); + Assert.assertEquals("Player A has lost because of commander damage", true, playerB.hasLost()); + } } \ No newline at end of file From ca19cd95f204916cc0bcdeaefe1c6b088f0fe4ff Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 15 Jun 2015 08:25:13 +0200 Subject: [PATCH 13/33] Mage release 1.4.1v2 --- Mage.Common/src/mage/utils/MageVersion.java | 2 +- Mage/src/mage/cards/repository/CardRepository.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index f30ec277dde..5a9acc7466c 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -42,7 +42,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 1; - public final static String MAGE_VERSION_MINOR_PATCH = "v1"; + public final static String MAGE_VERSION_MINOR_PATCH = "v2"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 357e18a204c..6cfd22889bb 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -60,7 +60,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 39; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 18; + private static final long CARD_CONTENT_VERSION = 19; private final Random random = new Random(); private Dao cardDao; From 8443b5ede113a94fd56956912795f3b1452662a6 Mon Sep 17 00:00:00 2001 From: wanderer120 Date: Mon, 15 Jun 2015 22:44:17 +0800 Subject: [PATCH 14/33] added greater harvester card. (p.s: i need this card, will add newer ones start from mirrodin after this ) --- .../mage/sets/darksteel/GreaterHarvester.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java diff --git a/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java b/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java new file mode 100644 index 00000000000..820cbf29fdd --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java @@ -0,0 +1,100 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.sets.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author wanderer120 + */ +public class GreaterHarvester extends CardImpl { + + public static final FilterPermanent filter = new FilterPermanent("a permanent"); + + static{ + filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.ENCHANTMENT), + new CardTypePredicate(CardType.PLANESWALKER), + new CardTypePredicate(CardType.LAND))); + } + + public GreaterHarvester(UUID ownerId) { + super(ownerId, 44, "Greater Harvester", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}{B}{B}"); + this.expansionSetCode = "DST"; + this.subtype.add("Horror"); + + this.power = new MageInt(5); + this.toughness = new MageInt(6); + + // At the beginning of your upkeep, sacrifice a permanent. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeEffect(filter, 1, ""), TargetController.YOU, false)); + + //Whenever Greater Harvester deals combat damage to a player, that player sacrifices two permanents. + this.addAbility(new GreaterHarvesterAbility()); + } + + public GreaterHarvester(final GreaterHarvester card) { + super(card); + } + + @Override + public GreaterHarvester copy() { + return new GreaterHarvester(this); + } +} +class GreaterHarvesterAbility extends TriggeredAbilityImpl { + + public GreaterHarvesterAbility() { + super(Zone.BATTLEFIELD, new SacrificeEffect(new FilterPermanent(), 2, "")); + } + + public GreaterHarvesterAbility(final GreaterHarvesterAbility ability) { + super(ability); + } + + @Override + public GreaterHarvesterAbility copy() { + return new GreaterHarvesterAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event instanceof DamagedPlayerEvent) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; + if (damageEvent.isCombatDamage() && event.getSourceId().equals(this.getSourceId())) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a player, that player sacrifices two permanent."; + } +} \ No newline at end of file From cd0f273122300e9c8dd0c6d740966089e59e3dcc Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 15 Jun 2015 17:35:48 +0200 Subject: [PATCH 15/33] Fixed a bug that if a human player had to discard more cards than he had on hand, the game UI was blocked. --- .../java/mage/client/table/TablesPanel.java | 1 + .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../abilities/activated/ReturnToHandTest.java | 1 - .../cards/abilities/keywords/DiscardTest.java | 39 ++++++++++++++----- Mage/src/mage/players/PlayerImpl.java | 3 +- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 62b0294b4cb..a92e06a60f0 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1146,6 +1146,7 @@ public class TablesPanel extends javax.swing.JPanel { options.setMatchTimeLimit(MatchTimeLimit.NONE); options.setFreeMulligans(2); options.setSkillLevel(SkillLevel.CASUAL); + options.setRollbackTurnsAllowed(true); table = session.createTable(roomId, options); session.joinTable(roomId, table.getTableId(), "Human", "Human", 1, DeckImporterUtil.importDeck("test.dck"),""); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 7daf4cb517b..49399a7889b 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -208,7 +208,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (log.isDebugEnabled()) { log.debug("chooseTarget: " + outcome.toString() + ":" + target.toString()); } - // sometimes a target aelection can be made from a player that does not control the ability + // sometimes a target selection can be made from a player that does not control the ability UUID abilityControllerId = playerId; if (target.getTargetController() != null && target.getAbilityController() != null) { abilityControllerId = target.getAbilityController(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java index 3b87ec843a1..7d4ea0d43cd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java @@ -27,7 +27,6 @@ */ package org.mage.test.cards.abilities.activated; -import mage.abilities.keyword.BloodthirstAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java index f9dcd57ecdd..146a5eb9256 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -37,16 +36,14 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class DiscardTest extends CardTestPlayerBase { - /* - * If Rest in Peace is in play, every card going to the graveyard goes to exile instead. - * If a card is discarded while Rest in Peace is on the battlefield, abilities that function - * when a card is discarded (such as madness) still work, even though that card never reaches - * a graveyard. - */ - + /* + * If Rest in Peace is in play, every card going to the graveyard goes to exile instead. + * If a card is discarded while Rest in Peace is on the battlefield, abilities that function + * when a card is discarded (such as madness) still work, even though that card never reaches + * a graveyard. + */ @Test public void testRestInPeaceAndCycle() { @@ -67,4 +64,26 @@ public class DiscardTest extends CardTestPlayerBase { assertHandCount(playerA, 1); // the card drawn by Cycling } -} \ No newline at end of file + /** + * With Bazaar of Baghdad, if you use it when you have no cards in hand, you + * draw 2, it asks for you to discard 3, but you can't. So the game can't + * progress and you lose on time. + */ + @Test + public void testBazaarOfBaghdad() { + // {T}: Draw two cards, then discard three cards. + addCard(Zone.BATTLEFIELD, playerA, "Bazaar of Baghdad", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw two cards, then discard three cards"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertHandCount(playerA, 0); + assertGraveyardCount(playerA, 2); + + } +} diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 732fe47137e..65228de1e6b 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -709,7 +709,8 @@ public abstract class PlayerImpl implements Player, Serializable { } } } else { - TargetDiscard target = new TargetDiscard(amount, amount, new FilterCard(CardUtil.numberToText(amount, "a") + " card" + (amount > 1 ? "s" : "")), playerId); + int possibleAmount = Math.min(getHand().size(), amount); + TargetDiscard target = new TargetDiscard(possibleAmount, possibleAmount, new FilterCard(CardUtil.numberToText(possibleAmount, "a") + " card" + (possibleAmount > 1 ? "s" : "")), playerId); choose(Outcome.Discard, target, source == null ? null : source.getSourceId(), game); for (UUID cardId : target.getTargets()) { Card card = this.getHand().get(cardId, game); From 9361b1c2e4c81388f0b23ce2887b1ba928c13c28 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 15 Jun 2015 20:09:33 +0200 Subject: [PATCH 16/33] [ORI] Updated mtg-cards-data.txt (80 cards). --- Utils/mtg-cards-data.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 3ae82c3d2eb..a2209b00837 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -26204,10 +26204,14 @@ Eagle of the Watch|Magic Origins|275|C|{2}{W}|Creature - Bird|2|1|Flying, vigila Serra Angel|Magic Origins|276|U|{3}{W}{W}{3}{W}{W}|Creature - Angel|4|44|4|Flying$Vigilance$Flying$Vigilance| Claustrophobia|Magic Origins|?|C|{1}{U}{U}{1}{U}{U}|Enchantment - Aura|||$Enchant creature$When Claustrophobia enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.$Enchant creature$When Claustrophobia enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.| Disperse|Magic Origins|?|C|{1}{U}{1}{U}|Instant|||Return target nonland permanent to its owner's hand.$Return target nonland permanent to its owner's hand.| +Displacement Wave|Magic Origins|?|?mtg-set-icon mtg-set-magic-origins-unknown|{X}{U}{U}{X}{U}{U}|Sorcery|||Return all nonland permanents with converted mana cost X or less to their owners' hands.$Return all nonland permanents with converted mana cost X or less to their owners' hands.| Maritime Guard|Magic Origins|?|C|{1}{U}{1}{U}|Creature - Merfolk Soldier|1|31|3|| +Mizzium Meddler|Magic Origins|?|R|{2}{U}{2}{U}|Creature - Vedalken Wizard|1|41|4|Flash$When Mizzium Meddler enters the battlefield, change a target of target spell or ability to Mizzium Meddler.$Flash$When Mizzium Meddler enters the battlefield, change a target of target spell or ability to Mizzium Meddler.| Scrapskin Drake|Magic Origins|?|C|{2}{U}{2}{U}|Creature - Zombie Drake|2|32|3|Flying$Scrapskin Drake can block only creatures with flying.$Flying$Scrapskin Drake can block only creatures with flying.| Tower Geist|Magic Origins|?|U|{3}{U}{3}{U}|Creature - Spirit|2|22|2|Flying$When Tower Geist enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.$Flying$When Tower Geist enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.| Hydrolash|Magic Origins|59|U|{2}{U}|Instant|||Attacking creatures get -2/-0 until end of turn.$Draw a card.| +Jace, Telepath Unbound|Magic Origins|60|M||Planeswalker - Jace|55|+1: Up to one target creature gets -2/-0 until your next turn.$-3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead.$-9: You get an emblem with "Whenever you cast a spell, target opponent puts the top five cards of his or her library into his or her graveyard". $+1: Up to one target creature gets -2/-0 until your next turn.$-3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead.$-9: You get an emblem with "Whenever you cast a spell, target opponent puts the top five cards of his or her library into his or her graveyard". | +Jace, Vryn's Prodigy|Magic Origins|60|M|{1}{U}{1}{U}|Legendary Creature - Human Wizard|0|20|2|{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn''s Prodigy, then return him to the battefield transformed under his owner's control. ${T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn''s Prodigy, then return him to the battefield transformed under his owner's control. | Jhessian Thief|Magic Origins|62|U|{2}{U}|Creature - Human Rogue|1|3|Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)$Whenever Jhessian Thief deals combat damage to a player, draw a card.| Ringwarden Owl|Magic Origins|068|C|{3}{U}{U}|Creature - Bird|3|3|Flying (This creature can't be blocked except by creatures with flying or reach.)$Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)| Separatist Voidmage|Magic Origins|72|C|{3}{U}|Creature - Human Wizard|2|2|When Separatist Voidmage enters the battlefield, you may return target creature to its owner's hand.| @@ -26237,6 +26241,8 @@ Titan's Strength|Magic Origins|?|C|{R}{R}|Instant|||Target creature gets +3/+1 u Avaricious Dragon|Magic Origins|131|M|{2}{R}{R}|Creature - Dragon|4|4|Flying$At the beginning of your draw step, draw an additional card.$At the beginning of your end step, discard your hand.| Bellows Lizard|Magic Origins|132|C|{R}{R}|Creature - Lizard|1|11|1|{1}{R}: Bellows Lizard gets +1/+0 until end of turn.${1}{R}: Bellows Lizard gets +1/+0 until end of turn.| Boggart Brute|Magic Origins|133|C|{2}{R}|Creature - Goblin Warrior|3|2|Menace (This creature can't be blocked except by two or more creatures.)| +Chandra, Fire of Kaladesh|Magic Origins|135|M|{1}{R}{R}{1}{R}{R}|Legendary Creature - Human Shaman|2|22|2|Whenever you cast a red spell, untap Chandra, Fire of Kaladesh.${T}: Chandra, Fire of Kaladesh deals 1 damage to target player. If Chandra has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.$Whenever you cast a red spell, untap Chandra, Fire of Kaladesh.${T}: Chandra, Fire of Kaladesh deals 1 damage to target player. If Chandra has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.| +Chandra, Roaring Flame|Magic Origins|135|M||Planeswalker - Chandra|44|+1: Chandra, Roaring Flame deals 2 damage to target player.$-2: Chandra, Roaring Flame deals 2 damage to target creature.$-7: Chandra, Roaring Flame deals 6 damage to each opponent. Each player dealt damage this way gets an emblem with "At the beginning of your upkeep, this emblem deals 3 damage to you."$+1: Chandra, Roaring Flame deals 2 damage to target player.$-2: Chandra, Roaring Flame deals 2 damage to target creature.$-7: Chandra, Roaring Flame deals 6 damage to each opponent. Each player dealt damage this way gets an emblem with "At the beginning of your upkeep, this emblem deals 3 damage to you."| Enthralling Victor|Magic Origins|142|U|{3}{R}|Creature - Human Warrior|3|2|When Enthralling Victor enters the battlefield, gain control of target creature an opponent controls with power 2 or less until end of turn. Untap that creature. It gains haste until end of turn.| Lightning Javelin|Magic Origins|153|C|{3}{R}|Sorcery|||Lightning Javelin deals 3 damage to target creature or player. Scry 1.| Seismic Elemental|Magic Origins|161|U|{3}{R}{R}|Creature - Elemental|4|4|When Seismic Elemental enters the battlefield, creatures without flying can't block this turn.| @@ -26253,6 +26259,8 @@ Conclave Naturalists|Magic Origins|171|U|{4}{G}|Creature - Dryad|4|4|When Concla Hitchclaw Recluse|Magic Origins|181|C|{2}{G}|Creature - Spider|1|4|Reach| Joraga Invocation|Magic Origins|183|U|{4}{G}{G}|Sorcery|||Each creature you control gets +3/+3 until end of turn and must be blocked this turn if able.| Mantle of Webs|Magic Origins|187|C|{1}{G}|Enchantment - Aura|||Enchant Creature$Enchanted creature gets +1/+3 and has reach.| +Nissa, Sage Animist|Magic Origins|189|M||Planeswalker - Nissa|33|+1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand.$-2: Put a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World onto the battlefield.$-7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands.$+1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand.$-2: Put a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World onto the battlefield.$-7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands.| +Nissa, Vastwood Seer|Magic Origins|189|M|{2}{G}{2}{G}|Legendary Creature - Elf Scout|2|22|2|When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library.$Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control.$When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library.$Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control.| Plummet|Magic Origins|286|C|{1}{G}{1}{G}|Instant|||Destroy target creature with flying.$Destroy target creature with flying.| Prized Unicorn|Magic Origins|287|U|{3}{G}{3}{G}|Creature - Unicorn|2|22|2|All creatures able to block Prized Unicorn do so.$All creatures able to block Prized Unicorn do so.| Terra Stomper|Magic Origins|288|R|{3}{G}{G}{G}{3}{G}{G}{G}|Creature - Beast|8|88|8|Terra Stomper can't be countered.$Trample$Terra Stomper can't be countered.$Trample| @@ -26260,7 +26268,7 @@ Meteorite|Magic Origins|?|U|{5}{5}|Artifact|||When Meteorite enters the battlefi Gold-Forged Sentinel|Magic Origins|?|U|{6}{6}|Artifact Creature - Chimera|4|44|4|Flying$Flying| Runed Servitor|Magic Origins|?|U|{2}{2}|Artifact Creature - Construct|2|22|2|When Runed Servitor dies, each player draws a card.$When Runed Servitor dies, each player draws a card.| Jayemdae Tome|Magic Origins|?|U|{4}{4}|Artifact|||{4}, {T}: Draw a card.${4}, {T}: Draw a card.| -Veteran's Sidearm|Magic Origins|242|C|{2}|Artifact - Equipment|||Equipped creature gets +1/+1.$Equip {1}| +Veteran's Sidearm|Magic Origins|242|C|{2}|Artifact - Equipment|||Equipped creature gets +1/+1.$Equip {1}| Ambush Commander|Duel Decks: Anthology, Elves vs. Goblins|1|R|{3}{G}{G}|Creature - Elf|2|2|Forests you control are 1/1 green Elf creatures that are still lands.${1}{G}, Sacrifice an Elf: Target creature gets +3/+3 until end of turn.| Lys Alana Huntmaster|Duel Decks: Anthology, Elves vs. Goblins|10|C|{2}{G}{G}|Creature - Elf Warrior|3|3|Whenever you cast an Elf spell, you may put a 1/1 green Elf Warrior creature token onto the battlefield.| Stonewood Invoker|Duel Decks: Anthology, Elves vs. Goblins|11|C|{1}{G}|Creature - Elf Mutant|2|2|{7}{G}: Stonewood Invoker gets +5/+5 until end of turn.| From 0e72d4a93f14705e68bb90328f96e7d03b7cab6a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 15 Jun 2015 23:45:07 +0200 Subject: [PATCH 17/33] [ORI] Added Sentinel of the Eternal Watch. --- .../SentinelOfTheEternalWatch.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/magicorigins/SentinelOfTheEternalWatch.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/SentinelOfTheEternalWatch.java b/Mage.Sets/src/mage/sets/magicorigins/SentinelOfTheEternalWatch.java new file mode 100644 index 00000000000..7571edab198 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/SentinelOfTheEternalWatch.java @@ -0,0 +1,98 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FirstTargetPointer; + +/** + * + * @author LevelX2 + */ +public class SentinelOfTheEternalWatch extends CardImpl { + + public SentinelOfTheEternalWatch(UUID ownerId) { + super(ownerId, 30, "Sentinel of the Eternal Watch", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{5}{W}"); + this.expansionSetCode = "ORI"; + this.subtype.add("Giant"); + this.subtype.add("Soldier"); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + // At the beginning of combat on each opponent's turn, tap target creature that player controls. + this.addAbility(new BeginningOfCombatTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect("target creature that player controls"), TargetController.OPPONENT, false, true)); + + } + + public SentinelOfTheEternalWatch(final SentinelOfTheEternalWatch card) { + super(card); + } + + @Override + public SentinelOfTheEternalWatch copy() { + return new SentinelOfTheEternalWatch(this); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof BeginningOfCombatTriggeredAbility) { + for (Effect effect: ability.getEffects()) { + UUID opponentId = effect.getTargetPointer().getFirst(game, ability); + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + effect.setTargetPointer(new FirstTargetPointer()); + FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature from the active opponent"); + filter.add(new ControllerIdPredicate(opponentId)); + Target target = new TargetCreaturePermanent(filter); + ability.addTarget(target); + } + } + } + } + + +} From 40e22d9f497123a11d9f4d00131d6ef6dbec6cee Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 16 Jun 2015 00:03:17 +0200 Subject: [PATCH 18/33] Some minor changes. --- .../mage/sets/darksteel/GreaterHarvester.java | 46 +++++++++++-------- .../sets/dragonsmaze/TeysaEnvoyOfGhosts.java | 19 ++++---- .../mirrodinbesieged/ContestedWarZone.java | 2 - 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java b/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java index 820cbf29fdd..b5dc2a20201 100644 --- a/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java +++ b/Mage.Sets/src/mage/sets/darksteel/GreaterHarvester.java @@ -22,6 +22,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.target.targetpointer.FixedTarget; /** @@ -29,17 +30,17 @@ import mage.target.targetpointer.FixedTarget; * @author wanderer120 */ public class GreaterHarvester extends CardImpl { - - public static final FilterPermanent filter = new FilterPermanent("a permanent"); - - static{ - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.CREATURE), - new CardTypePredicate(CardType.ENCHANTMENT), - new CardTypePredicate(CardType.PLANESWALKER), - new CardTypePredicate(CardType.LAND))); - } - + + public static final FilterPermanent filter = new FilterPermanent("a permanent"); + + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.ENCHANTMENT), + new CardTypePredicate(CardType.PLANESWALKER), + new CardTypePredicate(CardType.LAND))); + } + public GreaterHarvester(UUID ownerId) { super(ownerId, 44, "Greater Harvester", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}{B}{B}"); this.expansionSetCode = "DST"; @@ -50,7 +51,7 @@ public class GreaterHarvester extends CardImpl { // At the beginning of your upkeep, sacrifice a permanent. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeEffect(filter, 1, ""), TargetController.YOU, false)); - + //Whenever Greater Harvester deals combat damage to a player, that player sacrifices two permanents. this.addAbility(new GreaterHarvesterAbility()); } @@ -64,6 +65,7 @@ public class GreaterHarvester extends CardImpl { return new GreaterHarvester(this); } } + class GreaterHarvesterAbility extends TriggeredAbilityImpl { public GreaterHarvesterAbility() { @@ -79,16 +81,20 @@ class GreaterHarvesterAbility extends TriggeredAbilityImpl { return new GreaterHarvesterAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return EventType.DAMAGED_PLAYER.equals(event.getType()); + } + + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event instanceof DamagedPlayerEvent) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; - if (damageEvent.isCombatDamage() && event.getSourceId().equals(this.getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + if (damageEvent.isCombatDamage() && event.getSourceId().equals(this.getSourceId())) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } + return true; } return false; } @@ -97,4 +103,4 @@ class GreaterHarvesterAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever {this} deals combat damage to a player, that player sacrifices two permanent."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/TeysaEnvoyOfGhosts.java b/Mage.Sets/src/mage/sets/dragonsmaze/TeysaEnvoyOfGhosts.java index 720cbf0e21c..bab770d4b87 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/TeysaEnvoyOfGhosts.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/TeysaEnvoyOfGhosts.java @@ -100,16 +100,19 @@ class TeysaEnvoyOfGhostsTriggeredAbility extends TriggeredAbilityImpl { return new TeysaEnvoyOfGhostsTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event instanceof DamagedPlayerEvent) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; - Permanent p = game.getPermanent(event.getSourceId()); - if (damageEvent.getPlayerId().equals(controllerId) && damageEvent.isCombatDamage() && p != null && p.getCardType().contains(CardType.CREATURE)) { - game.getState().setValue(sourceId.toString(), p.getControllerId()); - getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; + Permanent sourcePermanent = game.getPermanent(event.getSourceId()); + if (damageEvent.getPlayerId().equals(getControllerId()) && damageEvent.isCombatDamage() && sourcePermanent != null && sourcePermanent.getCardType().contains(CardType.CREATURE)) { + game.getState().setValue(sourceId.toString(), sourcePermanent.getControllerId()); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/ContestedWarZone.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/ContestedWarZone.java index 18aa0237fd6..1558ed15381 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/ContestedWarZone.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/ContestedWarZone.java @@ -28,7 +28,6 @@ package mage.sets.mirrodinbesieged; import java.util.UUID; -import mage.Mana; import mage.constants.CardType; import mage.constants.Duration; @@ -46,7 +45,6 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.mana.ColorlessManaAbility; -import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.filter.common.FilterAttackingCreature; import mage.game.Game; From bb4f145bb6e8b6d84512e89423d5936d43e73089 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Fri, 12 Jun 2015 10:22:28 -0400 Subject: [PATCH 19/33] Initial work on Dreamborn Muse --- .../mage/sets/commander/DreambornMuse.java | 52 ++++++++++++++ .../src/mage/sets/legions/DreambornMuse.java | 69 +++++++++++++++++++ .../mage/sets/tenthedition/DreambornMuse.java | 52 ++++++++++++++ .../common/CardsInTargetHandCount.java | 31 +++++++++ 4 files changed, 204 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/commander/DreambornMuse.java create mode 100644 Mage.Sets/src/mage/sets/legions/DreambornMuse.java create mode 100644 Mage.Sets/src/mage/sets/tenthedition/DreambornMuse.java create mode 100644 Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java diff --git a/Mage.Sets/src/mage/sets/commander/DreambornMuse.java b/Mage.Sets/src/mage/sets/commander/DreambornMuse.java new file mode 100644 index 00000000000..93fbd3f00b2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander/DreambornMuse.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.commander; + +import java.util.UUID; + +/** + * + * @author anonymous + */ +public class DreambornMuse extends mage.sets.legions.DreambornMuse { + + public DreambornMuse(UUID ownerId) { + super(ownerId); + this.cardNumber = 44; + this.expansionSetCode = "CMD"; + } + + public DreambornMuse(final DreambornMuse card) { + super(card); + } + + @Override + public DreambornMuse copy() { + return new DreambornMuse(this); + } +} diff --git a/Mage.Sets/src/mage/sets/legions/DreambornMuse.java b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java new file mode 100644 index 00000000000..418e75355ec --- /dev/null +++ b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java @@ -0,0 +1,69 @@ +/* + * 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.sets.legions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardsInTargetHandCount; +import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; + +/** + * + * @author cbrianhill + */ +public class DreambornMuse extends CardImpl { + + public DreambornMuse(UUID ownerId) { + super(ownerId, 36, "Dreamborn Muse", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + this.expansionSetCode = "LGN"; + this.subtype.add("Spirit"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of each player's upkeep, that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PutLibraryIntoGraveTargetEffect(new CardsInTargetHandCount()), TargetController.ANY, false)); + } + + public DreambornMuse(final DreambornMuse card) { + super(card); + } + + @Override + public DreambornMuse copy() { + return new DreambornMuse(this); + } +} + +class DreambornMuseEffect { + +} diff --git a/Mage.Sets/src/mage/sets/tenthedition/DreambornMuse.java b/Mage.Sets/src/mage/sets/tenthedition/DreambornMuse.java new file mode 100644 index 00000000000..5971c0c184c --- /dev/null +++ b/Mage.Sets/src/mage/sets/tenthedition/DreambornMuse.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.tenthedition; + +import java.util.UUID; + +/** + * + * @author anonymous + */ +public class DreambornMuse extends mage.sets.legions.DreambornMuse { + + public DreambornMuse(UUID ownerId) { + super(ownerId); + this.cardNumber = 82; + this.expansionSetCode = "10E"; + } + + public DreambornMuse(final DreambornMuse card) { + super(card); + } + + @Override + public DreambornMuse copy() { + return new DreambornMuse(this); + } +} diff --git a/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java new file mode 100644 index 00000000000..4bff7fb7995 --- /dev/null +++ b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java @@ -0,0 +1,31 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author cbrianhill + */ +public class CardsInTargetHandCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player player = game.getPlayer(sourceAbility.getFirstTarget()); + return player.getHand().size(); + } + + @Override + public DynamicValue copy() { + return new CardsInTargetHandCount(); + } + + @Override + public String getMessage() { + return "cards in target player's hand"; + } + +} From b29bb5ac155d48f1d08fa3849573fb492b0e560b Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Mon, 15 Jun 2015 22:09:31 -0400 Subject: [PATCH 20/33] Finish adding Dreamborn Muse --- Mage.Sets/src/mage/sets/legions/DreambornMuse.java | 12 ++++++------ ...dCount.java => CardsInTargetPlayerHandCount.java} | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) rename Mage/src/mage/abilities/dynamicvalue/common/{CardsInTargetHandCount.java => CardsInTargetPlayerHandCount.java} (71%) diff --git a/Mage.Sets/src/mage/sets/legions/DreambornMuse.java b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java index 418e75355ec..15de5eceeb1 100644 --- a/Mage.Sets/src/mage/sets/legions/DreambornMuse.java +++ b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java @@ -30,7 +30,7 @@ package mage.sets.legions; import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.dynamicvalue.common.CardsInTargetHandCount; +import mage.abilities.dynamicvalue.common.CardsInTargetPlayerHandCount; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; @@ -49,9 +49,13 @@ public class DreambornMuse extends CardImpl { this.subtype.add("Spirit"); this.power = new MageInt(2); this.toughness = new MageInt(2); + + PutLibraryIntoGraveTargetEffect effect = new PutLibraryIntoGraveTargetEffect(new CardsInTargetPlayerHandCount()); + effect.setText("that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand."); // At the beginning of each player's upkeep, that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PutLibraryIntoGraveTargetEffect(new CardsInTargetHandCount()), TargetController.ANY, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.ANY, false)); + } public DreambornMuse(final DreambornMuse card) { @@ -63,7 +67,3 @@ public class DreambornMuse extends CardImpl { return new DreambornMuse(this); } } - -class DreambornMuseEffect { - -} diff --git a/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java similarity index 71% rename from Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java rename to Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java index 4bff7fb7995..8c7807eb0db 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetHandCount.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java @@ -10,17 +10,17 @@ import mage.players.Player; * * @author cbrianhill */ -public class CardsInTargetHandCount implements DynamicValue { +public class CardsInTargetPlayerHandCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - Player player = game.getPlayer(sourceAbility.getFirstTarget()); + Player player = game.getPlayer(effect.getTargetPointer().getFirst(game, sourceAbility)); return player.getHand().size(); } @Override public DynamicValue copy() { - return new CardsInTargetHandCount(); + return new CardsInTargetPlayerHandCount(); } @Override From d195c1e207d8a83b4937b192b75d884cafb2e0b0 Mon Sep 17 00:00:00 2001 From: emerald000 Date: Tue, 16 Jun 2015 02:10:26 -0400 Subject: [PATCH 21/33] Added Conclave Naturalists, Mantle of Webs and Veteran's Sidearm. --- .../magicorigins/ConclaveNaturalists.java | 68 +++++++++++++++ .../mage/sets/magicorigins/MantleOfWebs.java | 82 +++++++++++++++++++ .../sets/magicorigins/VeteransSidearm.java | 67 +++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/magicorigins/ConclaveNaturalists.java create mode 100644 Mage.Sets/src/mage/sets/magicorigins/MantleOfWebs.java create mode 100644 Mage.Sets/src/mage/sets/magicorigins/VeteransSidearm.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/ConclaveNaturalists.java b/Mage.Sets/src/mage/sets/magicorigins/ConclaveNaturalists.java new file mode 100644 index 00000000000..d7e12aefcca --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/ConclaveNaturalists.java @@ -0,0 +1,68 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; + +/** + * + * @author emerald000 + */ +public class ConclaveNaturalists extends CardImpl { + + public ConclaveNaturalists(UUID ownerId) { + super(ownerId, 171, "Conclave Naturalists", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{4}{G}"); + this.expansionSetCode = "ORI"; + this.subtype.add("Dryad"); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Conclave Naturalists enters the battlefield, you may destroy target artifact or enchantment. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); + this.addAbility(ability); + } + + public ConclaveNaturalists(final ConclaveNaturalists card) { + super(card); + } + + @Override + public ConclaveNaturalists copy() { + return new ConclaveNaturalists(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magicorigins/MantleOfWebs.java b/Mage.Sets/src/mage/sets/magicorigins/MantleOfWebs.java new file mode 100644 index 00000000000..5cd47e569ba --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/MantleOfWebs.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.magicorigins; + +import java.util.UUID; +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; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author emerald000 + */ +public class MantleOfWebs extends CardImpl { + + public MantleOfWebs(UUID ownerId) { + super(ownerId, 187, "Mantle of Webs", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + this.expansionSetCode = "ORI"; + 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); + + // Enchanted creature gets +1/+3 and has reach. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 3, Duration.WhileOnBattlefield))); + Effect effect = new GainAbilityAttachedEffect(ReachAbility.getInstance(), AttachmentType.AURA); + effect.setText("and has reach"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } + + public MantleOfWebs(final MantleOfWebs card) { + super(card); + } + + @Override + public MantleOfWebs copy() { + return new MantleOfWebs(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magicorigins/VeteransSidearm.java b/Mage.Sets/src/mage/sets/magicorigins/VeteransSidearm.java new file mode 100644 index 00000000000..554ce687644 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/VeteransSidearm.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 mage.sets.magicorigins; + +import java.util.UUID; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class VeteransSidearm extends CardImpl { + + public VeteransSidearm(UUID ownerId) { + super(ownerId, 242, "Veteran's Sidearm", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.expansionSetCode = "ORI"; + this.subtype.add("Equipment"); + + // Equipped creature gets +1/+1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 1))); + + // Equip {1} + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1))); + } + + public VeteransSidearm(final VeteransSidearm card) { + super(card); + } + + @Override + public VeteransSidearm copy() { + return new VeteransSidearm(this); + } +} From 9ddba9802f1a09920c7a8540abd9bd1cb8fec6be Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 16 Jun 2015 08:28:06 +0200 Subject: [PATCH 22/33] [ORI] Added Hixus, Prison Warden. --- .../sets/magicorigins/HixusPrisonWarden.java | 159 ++++++++++++++++++ Mage/src/mage/abilities/AbilityImpl.java | 2 +- 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/sets/magicorigins/HixusPrisonWarden.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/HixusPrisonWarden.java b/Mage.Sets/src/mage/sets/magicorigins/HixusPrisonWarden.java new file mode 100644 index 00000000000..acdb852b8b7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/HixusPrisonWarden.java @@ -0,0 +1,159 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class HixusPrisonWarden extends CardImpl { + + public HixusPrisonWarden(UUID ownerId) { + super(ownerId, 19, "Hixus, Prison Warden", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}{W}{3}{W}{W}"); + this.expansionSetCode = "ORI"; + this.supertype.add("Legendary"); + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(4); + this.toughness = new MageInt(44); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever a creature deals combat damage to you, if Hixus, Prison Warden entered the battlefield this turn, exile that creature until Hixus leaves the battlefield. + this.addAbility(new HixusPrisonWardenTriggeredAbility(new HixusPrisonWardenExileEffect())); + } + + public HixusPrisonWarden(final HixusPrisonWarden card) { + super(card); + } + + @Override + public HixusPrisonWarden copy() { + return new HixusPrisonWarden(this); + } +} + +class HixusPrisonWardenTriggeredAbility extends TriggeredAbilityImpl { + + public HixusPrisonWardenTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + this.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); + } + + public HixusPrisonWardenTriggeredAbility(final HixusPrisonWardenTriggeredAbility ability) { + super(ability); + } + + @Override + public HixusPrisonWardenTriggeredAbility copy() { + return new HixusPrisonWardenTriggeredAbility(this); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + MageObject mageObject = getSourceObject(game); + return (mageObject instanceof Permanent) && ((Permanent)mageObject).getTurnsOnBattlefield() ==1; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; + Permanent sourcePermanent = game.getPermanent(event.getSourceId()); + if (damageEvent.getPlayerId().equals(getControllerId()) && + damageEvent.isCombatDamage() && + sourcePermanent != null && + sourcePermanent.getCardType().contains(CardType.CREATURE)) { + getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature deals combat damage to you, if {this} entered the battlefield this turn, exile that creature until {this} leaves the battlefield."; + } + +} + +class HixusPrisonWardenExileEffect extends OneShotEffect { + + public HixusPrisonWardenExileEffect() { + super(Outcome.Benefit); + this.staticText = "exile that creature until {this} leaves the battlefield"; + } + + public HixusPrisonWardenExileEffect(final HixusPrisonWardenExileEffect effect) { + super(effect); + } + + @Override + public HixusPrisonWardenExileEffect copy() { + return new HixusPrisonWardenExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); + // If Prison Warden leaves the battlefield before its triggered ability resolves, + // the target creature won't be exiled. + if (permanent != null) { + return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName()).apply(game, source); + } + return false; + } +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index c7bbd061e53..bfc6bcd22bd 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -329,7 +329,7 @@ public abstract class AbilityImpl implements Ability { // and/or zones become the target of a spell trigger at this point; they'll wait to be put on // the stack until the spell has finished being cast.) - if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already TriggeredAbilities.checkTriggers() + if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in playerImpl.triggerAbility sourceObject.adjustTargets(this, game); } if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) { From d8df53dde74897c2db4a1d19e77c0903d926a940 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 16 Jun 2015 14:41:28 +0200 Subject: [PATCH 23/33] Added some tests. --- .../abilities/activated/ReturnToHandTest.java | 43 ++++++++++++++- .../abilities/keywords/LandfallTest.java | 55 ++++++++++++++++++- .../common/FilterCreatureForCombatBlock.java | 3 - 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java index 7d4ea0d43cd..9864c33cfdb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java @@ -73,5 +73,46 @@ public class ReturnToHandTest extends CardTestPlayerBase { } + /** + * Return from graveyard to hand if you play a swamp + */ + @Test + public void VeilbornGhoulTest1() { + // Veilborn Ghoul can't block. + // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. + addCard(Zone.GRAVEYARD, playerA, "Veilborn Ghoul"); + addCard(Zone.HAND, playerA, "Swamp"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerA, "Swamp", 1); + assertHandCount(playerA, "Veilborn Ghoul", 1); + + } + /** + * Return from graveyard to hand if you play a non swamp land but Urborg, Tomb of Yawgmoth is in play + */ + @Test + public void VeilbornGhoulTest2() { + // Veilborn Ghoul can't block. + // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. + addCard(Zone.GRAVEYARD, playerA, "Veilborn Ghoul"); + addCard(Zone.HAND, playerA, "Flood Plain"); + + // Each land is a Swamp in addition to its other land types. + addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flood Plain"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerA, "Flood Plain", 1); + assertHandCount(playerA, "Veilborn Ghoul", 1); + + } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java index 0f4ab1f663b..5e3b1b177cc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java @@ -146,5 +146,58 @@ public class LandfallTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Silvercoat Lion" , 1); } - + + @Test + public void testGroundswellWithoutLandfall() { + // Target creature gets +2/+2 until end of turn. + //Landfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead. + addCard(Zone.HAND, playerB, "Groundswell",1); + addCard(Zone.BATTLEFIELD, playerB, "Forest",1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion",1); + + attack(2, playerB, "Silvercoat Lion"); + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerB, "Groundswell", "Silvercoat Lion"); + + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerB, "Forest", 1); + assertGraveyardCount(playerB, "Groundswell" , 1); + + assertPermanentCount(playerB, "Silvercoat Lion" , 1); + assertPowerToughness(playerB, "Silvercoat Lion", 4, 4); + + assertLife(playerA, 16); // 2 + 4 + assertLife(playerB, 20); + } + + @Test + public void testGroundswellWithLandfall() { + // Target creature gets +2/+2 until end of turn. + //Landfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead. + addCard(Zone.HAND, playerB, "Groundswell",1); + addCard(Zone.HAND, playerB, "Forest"); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion",1); + + + playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Forest"); + attack(2, playerB, "Silvercoat Lion"); + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerB, "Groundswell", "Silvercoat Lion"); + + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerB, "Forest", 1); + assertGraveyardCount(playerB, "Groundswell" , 1); + + assertPermanentCount(playerB, "Silvercoat Lion" , 1); + assertPowerToughness(playerB, "Silvercoat Lion", 6, 6); + + assertLife(playerA, 14); // 2 + 4 + assertLife(playerB, 20); + } } diff --git a/Mage/src/mage/filter/common/FilterCreatureForCombatBlock.java b/Mage/src/mage/filter/common/FilterCreatureForCombatBlock.java index d8ad11777f9..fc9bb9edbfc 100644 --- a/Mage/src/mage/filter/common/FilterCreatureForCombatBlock.java +++ b/Mage/src/mage/filter/common/FilterCreatureForCombatBlock.java @@ -29,10 +29,7 @@ package mage.filter.common; import mage.constants.AsThoughEffectType; -import mage.filter.predicate.ObjectPlayer; -import mage.filter.predicate.ObjectPlayerPredicate; import mage.filter.predicate.Predicate; -import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.Permanent; From 27929cccba295507763a88531c0c575c58531d1c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 16 Jun 2015 17:01:15 +0200 Subject: [PATCH 24/33] Added handling to make it possible that a transformable card can enter the battlefield transformed. --- .../conditional/twofaced/TwoFacedCardEffectsTest.java | 6 ++++++ Mage/src/mage/abilities/keyword/TransformAbility.java | 3 +++ Mage/src/mage/game/permanent/PermanentCard.java | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/twofaced/TwoFacedCardEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/twofaced/TwoFacedCardEffectsTest.java index 4628fd55c10..69bd3a726a7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/twofaced/TwoFacedCardEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/twofaced/TwoFacedCardEffectsTest.java @@ -18,6 +18,12 @@ public class TwoFacedCardEffectsTest extends CardTestPlayerBase { */ @Test public void testEffectTurnedOffOnTransform() { + // Other Human creatures you control get +1/+1. + // At the beginning of each upkeep, if no spells were cast last turn, transform Mayor of Avabruck. + // Howlpack Alpha (transformed side) ---------------- + // Each other creature you control that's a Werewolf or a Wolf gets +1/+1. + // At the beginning of your end step, put a 2/2 green Wolf creature token onto the battlefield. + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Howlpack Alpha. addCard(Zone.BATTLEFIELD, playerA, "Mayor of Avabruck"); addCard(Zone.BATTLEFIELD, playerA, "Wolfir Avenger"); addCard(Zone.BATTLEFIELD, playerA, "Elite Inquisitor"); diff --git a/Mage/src/mage/abilities/keyword/TransformAbility.java b/Mage/src/mage/abilities/keyword/TransformAbility.java index 9b01d319c02..1d36731553d 100644 --- a/Mage/src/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/mage/abilities/keyword/TransformAbility.java @@ -44,6 +44,9 @@ public class TransformAbility extends SimpleStaticAbility { public static final String NO_SPELLS_TRANSFORM_RULE = "At the beginning of each upkeep, if no spells were cast last turn, transform {this}."; public static final String TWO_OR_MORE_SPELLS_TRANSFORM_RULE = "At the beginning of each upkeep, if a player cast two or more spells last turn, transform {this}."; + // this state value controlls if a permanent enters the battlefield already transformed + public static final String VALUE_KEY_ENTER_TRANSFORMED = "EnterTransformed"; + public TransformAbility() { super(Zone.BATTLEFIELD, new TransformEffect()); } diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index c6994cb72ed..af54c92b1dc 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -33,6 +33,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.keyword.TransformAbility; import mage.cards.Card; import mage.cards.LevelerCard; import mage.constants.Zone; @@ -65,6 +66,13 @@ public class PermanentCard extends PermanentImpl { if (card instanceof LevelerCard) { maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters(); } + if (canTransform()) { + if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null); + setTransformed(true); + TransformAbility.transform(this, getSecondCardFace(), game); + } + } } public PermanentCard(final PermanentCard permanent) { From a3dc7b6691d688ae8fe147505d6b808488b9be63 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 16 Jun 2015 17:01:56 +0200 Subject: [PATCH 25/33] [ORI] Added Nissa Sage Animist (coded by Emerald). --- .../sets/magicorigins/NissaSageAnimist.java | 198 ++++++++++++++++++ .../sets/magicorigins/NissaVastwoodSeer.java | 130 ++++++++++++ .../abilities/keywords/TransformTest.java | 68 ++++++ 3 files changed, 396 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java create mode 100644 Mage.Sets/src/mage/sets/magicorigins/NissaVastwoodSeer.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java new file mode 100644 index 00000000000..ff93fac4615 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java @@ -0,0 +1,198 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author emerald000 + */ +public class NissaSageAnimist extends CardImpl { + + public NissaSageAnimist(UUID ownerId) { + super(ownerId, 189, "Nissa, Sage Animist", Rarity.MYTHIC, new CardType[]{CardType.PLANESWALKER}, ""); + this.expansionSetCode = "ORI"; + this.subtype.add("Nissa"); + this.color.setGreen(true); + + this.nightCard = true; + this.canTransform = true; + + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); + + // +1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand. + this.addAbility(new LoyaltyAbility(new NissaSageAnimistPlusOneEffect(), 1)); + + // -2: Put a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World onto the battlefield. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new NissaSageAnimistToken()), -2)); + + // -7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands. + Ability ability = new LoyaltyAbility(new UntapTargetEffect(), -7); + ability.addTarget(new TargetLandPermanent(0, 6, new FilterLandPermanent("lands"), false)); + ability.addEffect(new NissaSageAnimistMinusSevenEffect()); + this.addAbility(ability); + } + + public NissaSageAnimist(final NissaSageAnimist card) { + super(card); + } + + @Override + public NissaSageAnimist copy() { + return new NissaSageAnimist(this); + } +} + +class NissaSageAnimistPlusOneEffect extends OneShotEffect { + + NissaSageAnimistPlusOneEffect() { + super(Outcome.Benefit); + this.staticText = "Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand."; + } + + NissaSageAnimistPlusOneEffect(final NissaSageAnimistPlusOneEffect effect) { + super(effect); + } + + @Override + public NissaSageAnimistPlusOneEffect copy() { + return new NissaSageAnimistPlusOneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && controller.getLibrary().size() > 0) { + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + CardsImpl cards = new CardsImpl(); + cards.add(card); + controller.revealCards("Nissa, Sage Animist", cards, game); + if (card.getCardType().contains(CardType.LAND)) { + return controller.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId()); + } else { + return controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); + } + } + return true; + } +} + +class NissaSageAnimistToken extends Token { + + NissaSageAnimistToken() { + super("Ashaya, the Awoken World", "legendary 4/4 green Elemental creature token named Ashaya, the Awoken World"); + this.setOriginalExpansionSetCode("ORI"); + this.getSupertype().add("Legendary"); + this.getPower().initValue(4); + this.getToughness().initValue(4); + this.color.setGreen(true); + this.getSubtype().add("Elemental"); + this.getCardType().add(CardType.CREATURE); + } +} + +class NissaSageAnimistMinusSevenEffect extends ContinuousEffectImpl { + + NissaSageAnimistMinusSevenEffect() { + super(Duration.EndOfGame, Outcome.BecomeCreature); + this.staticText = "They become 6/6 Elemental creatures. They're still lands"; + } + + NissaSageAnimistMinusSevenEffect(final NissaSageAnimistMinusSevenEffect effect) { + super(effect); + } + + @Override + public NissaSageAnimistMinusSevenEffect copy() { + return new NissaSageAnimistMinusSevenEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (UUID permanentId : this.getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (!permanent.getCardType().contains(CardType.CREATURE)) { + permanent.getCardType().add(CardType.CREATURE); + } + if (!permanent.getSubtype().contains("Elemental")) { + permanent.getSubtype().add("Elemental"); + } + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getToughness().setValue(6); + permanent.getPower().setValue(6); + } + } + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 || layer == Layer.PTChangingEffects_7; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magicorigins/NissaVastwoodSeer.java b/Mage.Sets/src/mage/sets/magicorigins/NissaVastwoodSeer.java new file mode 100644 index 00000000000..22ef98bc763 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/NissaVastwoodSeer.java @@ -0,0 +1,130 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition.CountType; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author emerald000 + */ +public class NissaVastwoodSeer extends CardImpl { + + private static final FilterCard filter = new FilterCard("basic Forest card"); + static { + filter.add(new SupertypePredicate("Basic")); + filter.add(new SubtypePredicate("Forest")); + } + + public NissaVastwoodSeer(UUID ownerId) { + super(ownerId, 189, "Nissa, Vastwood Seer", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "ORI"; + this.supertype.add("Legendary"); + this.subtype.add("Elf"); + this.subtype.add("Scout"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + this.canTransform = true; + this.secondSideCard = new NissaSageAnimist(ownerId); + + // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true), true)); + + // Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control. + this.addAbility(new TransformAbility()); + this.addAbility(new ConditionalTriggeredAbility( + new EntersBattlefieldControlledTriggeredAbility(new NissaVastwoodSeerTransformEffect(), new FilterLandPermanent()), + new PermanentsOnTheBattlefieldCondition(new FilterLandPermanent(), CountType.MORE_THAN, 6, true), + "Whenever a land enters the battlefield under your control, if you control seven or more lands, exile {this}, then return her to the battlefield transformed under her owner's control.")); + } + + public NissaVastwoodSeer(final NissaVastwoodSeer card) { + super(card); + } + + @Override + public NissaVastwoodSeer copy() { + return new NissaVastwoodSeer(this); + } +} + +class NissaVastwoodSeerTransformEffect extends OneShotEffect { + + NissaVastwoodSeerTransformEffect() { + super(Outcome.Benefit); + this.staticText = "exile {this}, then return her to the battlefield transformed under her owner's control"; + } + + NissaVastwoodSeerTransformEffect(final NissaVastwoodSeerTransformEffect effect) { + super(effect); + } + + @Override + public NissaVastwoodSeerTransformEffect copy() { + return new NissaVastwoodSeerTransformEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + Player controller = game.getPlayer(source.getControllerId()); + if (sourceObject != null && controller != null) { + Card card = (Card) sourceObject; + if (controller.moveCards(card, Zone.BATTLEFIELD, Zone.EXILED, source, game)) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + controller.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java new file mode 100644 index 00000000000..1aa83fc7929 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java @@ -0,0 +1,68 @@ +/* + * 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 mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class TransformTest extends CardTestPlayerBase{ + + @Test + public void NissaVastwoodSeerTest() { + + addCard(Zone.LIBRARY, playerA, "Forest"); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library. + // Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control. + + addCard(Zone.HAND, playerA, "Nissa, Vastwood Seer"); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nissa, Vastwood Seer"); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Forest", 7); + + assertPermanentCount(playerA, "Nissa, Vastwood Seer", 0); + assertPermanentCount(playerA, "Nissa, Sage Animist", 1); + assertCounterCount("Nissa, Sage Animist", CounterType.LOYALTY, 3); + } + +} \ No newline at end of file From c987ae7a21920f75acb2c5bf5f342868feb94d44 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 00:54:26 +0200 Subject: [PATCH 26/33] [ORI] Added Grasp of the Hieromancer and Heavy Infantry. --- .../magicorigins/GraspOfTheHieromancer.java | 138 ++++++++++++++++++ .../mage/sets/magicorigins/HeavyInfantry.java | 77 ++++++++++ .../sets/riseoftheeldrazi/SnakeUmbra.java | 9 +- 3 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/magicorigins/GraspOfTheHieromancer.java create mode 100644 Mage.Sets/src/mage/sets/magicorigins/HeavyInfantry.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/GraspOfTheHieromancer.java b/Mage.Sets/src/mage/sets/magicorigins/GraspOfTheHieromancer.java new file mode 100644 index 00000000000..811fabbf54a --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/GraspOfTheHieromancer.java @@ -0,0 +1,138 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class GraspOfTheHieromancer extends CardImpl { + + public GraspOfTheHieromancer(UUID ownerId) { + super(ownerId, 15, "Grasp of the Hieromancer", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "ORI"; + 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); + + // Enchanted creature gets +1/+1 and has "Whenever this creature attacks, tap target creature defending player controls." + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); + Ability gainedAbility = new GraspOfTheHieromancerTriggeredAbility(new TapTargetEffect(), false); + gainedAbility.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls"))); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); + effect.setText("and has \"Whenever this creature attacks, tap target creature defending player controls.\""); + ability.addEffect(effect); + this.addAbility(ability); + + } + + public GraspOfTheHieromancer(final GraspOfTheHieromancer card) { + super(card); + } + + @Override + public GraspOfTheHieromancer copy() { + return new GraspOfTheHieromancer(this); + } +} + +class GraspOfTheHieromancerTriggeredAbility extends TriggeredAbilityImpl { + + + + public GraspOfTheHieromancerTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + public GraspOfTheHieromancerTriggeredAbility(final GraspOfTheHieromancerTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getCombat().getAttackers().contains(getSourceId()) ) { + UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(getSourceId(), game); + if (defendingPlayerId != null) { + this.getTargets().clear(); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); + UUID defenderId = game.getCombat().getDefenderId(getSourceId()); + filter.add(new ControllerIdPredicate(defenderId)); + TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + this.addTarget(target); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} attacks, tap target creature defending player controls."; + } + + @Override + public GraspOfTheHieromancerTriggeredAbility copy() { + return new GraspOfTheHieromancerTriggeredAbility(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/magicorigins/HeavyInfantry.java b/Mage.Sets/src/mage/sets/magicorigins/HeavyInfantry.java new file mode 100644 index 00000000000..5640bfc4bc4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magicorigins/HeavyInfantry.java @@ -0,0 +1,77 @@ +/* + * 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.sets.magicorigins; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class HeavyInfantry extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); + + static { + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + } + + public HeavyInfantry(UUID ownerId) { + super(ownerId, 18, "Heavy Infantry", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{4}{W}"); + this.expansionSetCode = "ORI"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Heavy Infantry enters the battlefield, tap target creature an opponent controls. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public HeavyInfantry(final HeavyInfantry card) { + super(card); + } + + @Override + public HeavyInfantry copy() { + return new HeavyInfantry(this); + } +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SnakeUmbra.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SnakeUmbra.java index 45efa75b0a4..5daad05f235 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SnakeUmbra.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SnakeUmbra.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.constants.*; 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.DealsDamageToOpponentTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -54,7 +55,6 @@ public class SnakeUmbra extends CardImpl { this.expansionSetCode = "ROE"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -63,11 +63,14 @@ public class SnakeUmbra extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+1 - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield))); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); // and has "Whenever this creature deals damage to an opponent, you may draw a card." Ability gainedAbility = new DealsDamageToOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1), true); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA))); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); + effect.setText("and has \"Whenever this creature deals damage to an opponent, you may draw a card.\""); + ability.addEffect(effect); + this.addAbility(ability); // Totem armor this.addAbility(new TotemArmorAbility()); From ae2eb8bb5022b603fe0bc41a252e33e4968f1d78 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 00:57:28 +0200 Subject: [PATCH 27/33] Minor changes to Dreamborn Muse. --- Mage.Sets/src/mage/sets/legions/DreambornMuse.java | 3 +-- .../dynamicvalue/common/CardsInTargetPlayerHandCount.java | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/legions/DreambornMuse.java b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java index 15de5eceeb1..f2e6b86c256 100644 --- a/Mage.Sets/src/mage/sets/legions/DreambornMuse.java +++ b/Mage.Sets/src/mage/sets/legions/DreambornMuse.java @@ -50,10 +50,9 @@ public class DreambornMuse extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + // At the beginning of each player's upkeep, that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand. PutLibraryIntoGraveTargetEffect effect = new PutLibraryIntoGraveTargetEffect(new CardsInTargetPlayerHandCount()); effect.setText("that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand."); - - // At the beginning of each player's upkeep, that player puts the top X cards of his or her library into his or her graveyard, where X is the number of cards in his or her hand. this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.ANY, false)); } diff --git a/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java index 8c7807eb0db..27bab074560 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/CardsInTargetPlayerHandCount.java @@ -15,7 +15,10 @@ public class CardsInTargetPlayerHandCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Player player = game.getPlayer(effect.getTargetPointer().getFirst(game, sourceAbility)); - return player.getHand().size(); + if (player != null) { + return player.getHand().size(); + } + return 0; } @Override From 568ab914ab7872446ac9ef59f403bc5ebeb67939 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 08:07:00 +0200 Subject: [PATCH 28/33] [ORI] Updated mtg-cards-data.txt (84 cards). --- Utils/mtg-cards-data.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index a2209b00837..8073fae95d8 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -26196,6 +26196,8 @@ Akroan Jailer|Magic Origins|1|C|{W}|Creature - Human Soldier|1|1|{2}{W}, {T}: Ta Grasp of the Hieromancer|Magic Origins|15|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature attacks, tap target creature defending player controls."| Heavy Infantry|Magic Origins|18|C|{4}{W}|Creature - Human Soldier|3|4|When Heavy Infantry enters the battlefield, tap target creature an opponent controls.| Hixus, Prison Warden|Magic Origins|19|R|{3}{W}{W}{3}{W}{W}|Legendary Creature - Human Soldier|4|44|4|Flash$Whenever a creature deals combat damage to you, if Hixus, Prison Warden entered the battlefield this turn, exile that creature until Hixus leaves the battlefield.$Flash$Whenever a creature deals combat damage to you, if Hixus, Prison Warden entered the battlefield this turn, exile that creature until Hixus leaves the battlefield.| +Gideon, Battle-Forged|Magic Origins|023|M||Planeswalker - Gideon|3|+2: Up to one target creature an opponent controls attacks Gideon, Battle-Forged during its controller's next turn if able.$+1: Until your next turn, target creature gains indestructible. Untap that creature.$0: Until end of turn, Gideon, Battle-Forged becomes a 4/4 Human Soldier creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn.| +Kytheon, Hero of Akros|Magic Origins|23|M|{W}|Legendary Creature - Human Soldier|2|1|At end of combat, if Kytheon, Hero of Akros and at least two other creatures attacked this combat, exile Kytheon, then return him to the battlefield transformed under his owner's control.${2}{W}: Kytheon gains indestructible until end of turn.| Sentinel of the Eternal Watch|Magic Origins|30|U|{5}{W}|Creature - Giant Soldier|4|6|Vigilance (Attacking doesn't cause this creature to tap.)$At the beginning of combat on each opponent's turn, tap target creature that player controls.| Valor in Akros|Magic Origins|39|U|{3}{W}|Enchantment|||Whenever a creature enters the battlefield under your control, creatures you control get +1/+1 until end of turn.| Aegis Angel|Magic Origins|273|R|{4}{W}{W}{4}{W}{W}|Creature - Angel|5|55|5|Flying$When Aegis Angel enters the battlefield, another target permanent gains indestructible for as long as you control Aegis Angel.$Flying$When Aegis Angel enters the battlefield, another target permanent gains indestructible for as long as you control Aegis Angel.| @@ -26235,8 +26237,10 @@ Nightmare|Magic Origins|282|R|{5}{B}{5}{B}|Creature - Nightmare Horse|0|00|0|Fly Sengir Vampire|Magic Origins|283|U|{3}{B}{B}{3}{B}{B}|Creature - Vampire|4|44|4|Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.$Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.| Act of Treason|Magic Origins|?|C|{2}{R}{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.$Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.| Chandra's Fury|Magic Origins|?|C|{4}{R}{4}{R}|Instant|||Chandra's Fury deals 4 damage to target player and 1 damage to each creature that player controls.$Chandra's Fury deals 4 damage to target player and 1 damage to each creature that player controls.| +Chandra's Ignition|Magic Origins|?|R|{3}{R}{R}{3}{R}{R}|Sorcery|||Target creature you control deals damage equal to its power to each other creature and each opponent.$Target creature you control deals damage equal to its power to each other creature and each opponent.| Cobblebrute|Magic Origins|?|C|{3}{R}{3}{R}|Creature - Elemental|5|25|2|| Fiery Conclusion|Magic Origins|?|C|{1}{R}{1}{R}|Instant|||As an additional cost to cast Fiery Conclusion, sacrifice a creature.$Fiery Conclusion deals 5 damage to target creature.$As an additional cost to cast Fiery Conclusion, sacrifice a creature.$Fiery Conclusion deals 5 damage to target creature.| +Ravaging Blaze|Magic Origins|?|U|{X}{R}{R}{X}{R}{R}|Instant|||Ravaging Blaze deals X damage to target creature. $Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Ravaging Blaze also deals X damage to that creature's controller.$Ravaging Blaze deals X damage to target creature. $Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Ravaging Blaze also deals X damage to that creature's controller.| Titan's Strength|Magic Origins|?|C|{R}{R}|Instant|||Target creature gets +3/+1 until end of turn. Scry 1.$Target creature gets +3/+1 until end of turn. Scry 1.| Avaricious Dragon|Magic Origins|131|M|{2}{R}{R}|Creature - Dragon|4|4|Flying$At the beginning of your draw step, draw an additional card.$At the beginning of your end step, discard your hand.| Bellows Lizard|Magic Origins|132|C|{R}{R}|Creature - Lizard|1|11|1|{1}{R}: Bellows Lizard gets +1/+0 until end of turn.${1}{R}: Bellows Lizard gets +1/+0 until end of turn.| From e934640b5d7c4a2554cea2d592c447bbef9e754b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 14:54:57 +0200 Subject: [PATCH 29/33] * Tectonic Edge - Fixed that the number of lands condition was checked as a sum of all opponents instead of a separate check for each opponent on its own. --- .../OpponentControlsPermanentCondition.java | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/Mage/src/mage/abilities/condition/common/OpponentControlsPermanentCondition.java b/Mage/src/mage/abilities/condition/common/OpponentControlsPermanentCondition.java index dfb0f89be91..efb94d0c482 100644 --- a/Mage/src/mage/abilities/condition/common/OpponentControlsPermanentCondition.java +++ b/Mage/src/mage/abilities/condition/common/OpponentControlsPermanentCondition.java @@ -28,31 +28,29 @@ package mage.abilities.condition.common; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; -import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; /** - * + * Checks if one opponent (each opponent is checked on its own) fulfills + * the defined condition of controlling the defined number of permanents. + * * @author LevelX2 */ public class OpponentControlsPermanentCondition implements Condition { public static enum CountType { MORE_THAN, FEWER_THAN, EQUAL_TO }; + private FilterPermanent filter; - private Condition condition; private CountType type; private int count; /** - * Applies a filter and delegates creation to - * {@link #ControlsPermanent(mage.filter.FilterPermanent, mage.abilities.condition.common.ControlsPermanent.CountType, int)} - * with {@link CountType#MORE_THAN}, and 0. - * * @param filter */ public OpponentControlsPermanentCondition(FilterPermanent filter) { @@ -74,46 +72,33 @@ public class OpponentControlsPermanentCondition implements Condition { this.count = count; } - /** - * Applies a filter, a {@link CountType}, and count to permanents on the - * battlefield and calls the decorated condition to see if it - * {@link #apply(mage.game.Game, mage.abilities.Ability) applies} - * as well. This will force both conditions to apply for this to be true. - * - * @param filter - * @param type - * @param count - * @param conditionToDecorate - */ - public OpponentControlsPermanentCondition ( FilterPermanent filter, CountType type, int count, Condition conditionToDecorate ) { - this(filter, type, count); - this.condition = conditionToDecorate; - } - @Override public boolean apply(Game game, Ability source) { - boolean conditionApplies = false; - - FilterPermanent localFilter = filter.copy(); - localFilter.add(new ControllerPredicate(TargetController.OPPONENT)); - - switch ( this.type ) { - case FEWER_THAN: - conditionApplies = game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) < this.count; - break; - case MORE_THAN: - conditionApplies = game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) > this.count; - break; - case EQUAL_TO: - conditionApplies = game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) == this.count; - break; + boolean conditionApplies = false; + for(UUID opponentId :game.getOpponents(source.getControllerId())) { + FilterPermanent localFilter = filter.copy(); + localFilter.add(new ControllerIdPredicate(opponentId)); + switch ( this.type ) { + case FEWER_THAN: + if (game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) < this.count) { + conditionApplies = true; + break; + } + case MORE_THAN: + if (game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) > this.count) { + conditionApplies = true; + break; + } + break; + case EQUAL_TO: + if (game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game) == this.count) { + conditionApplies = true; + break; + } + break; + } + } - - //If a decorated condition exists, check it as well and apply them together. - if ( this.condition != null ) { - conditionApplies = conditionApplies && this.condition.apply(game, source); - } - return conditionApplies; } From 9882948a069783ef3a41be474f94a2e511ff6d92 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 16:15:44 +0200 Subject: [PATCH 30/33] * Splice on Arcane - Fixed that if a player used an alternate cost to cast the arcane spell (e.g. Nourishing Shoal) splice mana costs were also removed. --- .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../keywords/SpliceOnArcaneTest.java | 144 ++++++++++++++++++ .../keyword/SpliceOntoArcaneAbility.java | 15 +- 3 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 49399a7889b..1ba65e80b42 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -507,7 +507,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetDiscard || target instanceof TargetCardInHand) { if (outcome.isGood()) { ArrayList cardsInHand = new ArrayList<>(hand.getCards(game)); - while (!target.isChosen() && !cardsInHand.isEmpty() && target.getMaxNumberOfTargets() < target.getTargets().size()) { + while (!target.isChosen() && !cardsInHand.isEmpty() && target.getMaxNumberOfTargets() > target.getTargets().size()) { Card card = pickBestCard(cardsInHand, null, target, source, game); if (card != null) { if (target.canTarget(card.getId(), source, game)) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java new file mode 100644 index 00000000000..6eff8eaae26 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java @@ -0,0 +1,144 @@ +/* + * 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.abilities.keyword.HasteAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class SpliceOnArcaneTest extends CardTestPlayerBase { + + /** + * Test that it works to cast Through the Breach + * by slicing it on an arcane spell + * + */ + @Test + public void testSpliceThroughTheBreach() { + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // Sorcery - Arcane {R} + // Lava Spike deals 3 damage to target player. + addCard(Zone.HAND, playerA, "Lava Spike",1); + // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. + // Splice onto Arcane {2}{R}{R} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) + addCard(Zone.HAND, playerA, "Through the Breach",1); + addCard(Zone.HAND, playerA, "Silvercoat Lion",1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lava Spike", playerB); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + assertGraveyardCount(playerA, "Lava Spike", 1); + assertHandCount(playerA, "Through the Breach", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); + Assert.assertEquals("All available mana has to be used", 0, playerA.getManaAvailable(currentGame).size()); + } + + @Test + public void testSpliceTorrentOfStone() { + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Sorcery - Arcane {R} + // Lava Spike deals 3 damage to target player. + addCard(Zone.HAND, playerA, "Lava Spike",1); + // Torrent of Stone deals 4 damage to target creature. + // Splice onto Arcane-Sacrifice two Mountains. (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) + addCard(Zone.HAND, playerA, "Torrent of Stone",1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion",1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lava Spike", playerB); + addTarget(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + assertGraveyardCount(playerA, "Lava Spike", 1); + assertHandCount(playerA, "Torrent of Stone", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Mountain", 0); + Assert.assertEquals("No more mana available", "[]", playerA.getManaAvailable(currentGame).toString()); + } + /** + * Nourishing Shoal's interaction with Splicing Through the Breach is + * bugged. You should still need to pay 2RR as an additional cost, which is + * not affected by the alternate casting method of Shoal, but you are able + * to Splice it for free. This is a very relevant bug right now due to the + * appearance of the deck over the weekend, and it makes the deck absurdly + * powerful. + */ + @Test + public void testSpliceThroughTheBreach2() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. + // You gain X life. + addCard(Zone.HAND, playerA, "Nourishing Shoal",1); + addCard(Zone.HAND, playerA, "Giant Growth",1); + // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. + // Splice onto Arcane {2}{R}{R} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) + addCard(Zone.HAND, playerA, "Through the Breach",1); + addCard(Zone.HAND, playerA, "Silvercoat Lion",1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nourishing Shoal"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 21); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Nourishing Shoal", 1); + assertHandCount(playerA, "Through the Breach", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); + + Assert.assertEquals("All available mana has to be used","[]", playerA.getManaAvailable(currentGame).toString()); + } + + +} diff --git a/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java b/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java index 92d7ee0474c..06d6c201bf1 100644 --- a/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java +++ b/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java @@ -38,7 +38,6 @@ import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.SpliceCardEffectImpl; import mage.cards.Card; -import mage.cards.CardsImpl; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SpellAbilityType; @@ -105,12 +104,12 @@ import mage.players.Player; public class SpliceOntoArcaneAbility extends SimpleStaticAbility { private static final String KEYWORD_TEXT = "Splice onto Arcane"; - private Costs spliceCosts = new CostsImpl(); + private Costs spliceCosts = new CostsImpl<>(); private boolean nonManaCosts = false; public SpliceOntoArcaneAbility(String manaString) { super(Zone.HAND, new SpliceOntoArcaneEffect()); - spliceCosts.add(new ManaCostsImpl(manaString)); + spliceCosts.add(new ManaCostsImpl<>(manaString)); } public SpliceOntoArcaneAbility(Cost cost) { @@ -144,7 +143,6 @@ public class SpliceOntoArcaneAbility extends SimpleStaticAbility { } } - class SpliceOntoArcaneEffect extends SpliceCardEffectImpl { public SpliceOntoArcaneEffect() { @@ -156,8 +154,6 @@ class SpliceOntoArcaneEffect extends SpliceCardEffectImpl { super(effect); } - - @Override public SpliceOntoArcaneEffect copy() { return new SpliceOntoArcaneEffect(this); @@ -175,12 +171,7 @@ class SpliceOntoArcaneEffect extends SpliceCardEffectImpl { splicedAbility.setSourceId(abilityToModify.getSourceId()); spell.addSpellAbility(splicedAbility); for (Iterator it = ((SpliceOntoArcaneAbility) source).getSpliceCosts().iterator(); it.hasNext();) { - Cost cost = (Cost) it.next(); - if (cost instanceof ManaCostsImpl) { - spell.getSpellAbility().getManaCostsToPay().add((ManaCostsImpl) cost.copy()); - } else { - spell.getSpellAbility().getCosts().add(cost.copy()); - } + spell.getSpellAbility().getCosts().add(((Cost) it.next()).copy()); } } return true; From 68a0782e30e5577b5fb1f1d2913e38fd1cfd005c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 17:41:20 +0200 Subject: [PATCH 31/33] Added a test. --- .../sets/zendikar/PyromancerAscension.java | 20 ++++++---- .../cards/abilities/keywords/StormTest.java | 37 ++++++++++++++++++- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/zendikar/PyromancerAscension.java b/Mage.Sets/src/mage/sets/zendikar/PyromancerAscension.java index dc5f38f7122..5eb3676dc2a 100644 --- a/Mage.Sets/src/mage/sets/zendikar/PyromancerAscension.java +++ b/Mage.Sets/src/mage/sets/zendikar/PyromancerAscension.java @@ -38,14 +38,10 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import mage.target.TargetSpell; import mage.target.targetpointer.FixedTarget; /** @@ -91,9 +87,14 @@ class PyromancerAscensionQuestTriggeredAbility extends TriggeredAbilityImpl { return new PyromancerAscensionQuestTriggeredAbility(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 && event.getPlayerId().equals(this.getControllerId())) { + if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (isControlledInstantOrSorcery(spell)) { Card sourceCard = game.getCard(spell.getSourceId()); @@ -138,10 +139,15 @@ class PyromancerAscensionCopyTriggeredAbility extends TriggeredAbilityImpl { public PyromancerAscensionCopyTriggeredAbility copy() { return new PyromancerAscensionCopyTriggeredAbility(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 && event.getPlayerId().equals(this.getControllerId())) { + if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (isControlledInstantOrSorcery(spell)) { Permanent permanent = game.getBattlefield().getPermanent(this.getSourceId()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/StormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/StormTest.java index 9157c8dfa34..6caa1dcf763 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/StormTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/StormTest.java @@ -30,6 +30,7 @@ package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -163,5 +164,39 @@ public class StormTest extends CardTestPlayerBase { assertLife(playerB, 16); // 3 (Lightning Bolt) + 1 from Storm copied Grapeshot } - + + /** + * I provide a game log fo the issue with storm mentioned earlier. I guess Pyromancer Ascension is a culprit. + * + * + */ + @Test + public void testStormAndPyromancerAscension() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // Whenever you cast an instant or sorcery spell that has the same name as a card in your graveyard, you may put a quest counter on Pyromancer Ascension. + // Whenever you cast an instant or sorcery spell while Pyromancer Ascension has two or more quest counters on it, you may copy that spell. You may choose new targets for the copy. + addCard(Zone.BATTLEFIELD, playerA, "Pyromancer Ascension", 1); + // Grapeshot deals 1 damage to target creature or player. - Sorcery {1}{R} + // Storm (When you cast this spell, copy it for each spell cast before it this turn. You may choose new targets for the copies.) + addCard(Zone.LIBRARY, playerA, "Grapeshot", 2); + skipInitShuffling(); + // Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library. + addCard(Zone.HAND, playerA, "Sleight of Hand"); + addCard(Zone.HAND, playerA, "Shock", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sleight of Hand"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", "targetPlayer=PlayerB", "Shock", StackClause.WHILE_NOT_ON_STACK); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", "targetPlayer=PlayerB", "Shock", StackClause.WHILE_NOT_ON_STACK); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grapeshot", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Shock", 3); + assertGraveyardCount(playerA, "Grapeshot", 1); + assertCounterCount("Pyromancer Ascension", CounterType.QUEST, 2); + assertLife(playerB, 8); // 6 from the Shocks + 5 from Grapeshot + 1 from Pyromancer Ascencsion copy + } } From 2d9e5a032e6c3f2a66ddfed00e9e9675017fa577 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 21:06:07 +0200 Subject: [PATCH 32/33] * Descendants' Path - Fixed that it did not work with Chnagelling or Has all creature types abililities. --- .../sets/avacynrestored/DescendantsPath.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DescendantsPath.java b/Mage.Sets/src/mage/sets/avacynrestored/DescendantsPath.java index d181d91f344..a6f0e7964a5 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DescendantsPath.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DescendantsPath.java @@ -27,7 +27,6 @@ */ package mage.sets.avacynrestored; -import java.util.ArrayList; import java.util.UUID; import mage.constants.CardType; @@ -42,11 +41,10 @@ import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -59,7 +57,6 @@ public class DescendantsPath extends CardImpl { super(ownerId, 173, "Descendants' Path", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); this.expansionSetCode = "AVR"; - // At the beginning of your upkeep, reveal the top card of your library. If it's a creature card that shares a creature type with a creature you control, you may cast that card without paying its mana cost. Otherwise, put that card on the bottom of your library. Ability ability = new BeginningOfUpkeepTriggeredAbility(new DescendantsPathEffect(), TargetController.YOU, false); this.addAbility(ability); @@ -93,36 +90,39 @@ class DescendantsPathEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (player.getLibrary().size() > 0) { - Card card = player.getLibrary().getFromTop(game); - player.revealCards("DescendantsPath", new CardsImpl(card), game); - if (card.getCardType().contains(CardType.CREATURE)) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + if (controller.getLibrary().size() > 0) { + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); + if (card.getCardType().contains(CardType.CREATURE)) { FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - - ArrayList> subtypes = new ArrayList<>(); - for (String subtype: card.getSubtype()) { - subtypes.add(new SubtypePredicate(subtype)); + boolean found = false; + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { + if (CardUtil.shareSubtypes(card, permanent)) { + found = true; + break; + } } - filter.add(Predicates.or(subtypes)); - - int count = game.getBattlefield().getAllActivePermanents(filter, player.getId(), game).size(); - if (count > 0) { - game.informPlayers("DescendantsPath: Found a creature that shares a creature type with the revealed card."); - if (player.chooseUse(Outcome.Benefit, "Cast the card?", game)) { - player.cast(card.getSpellAbility(), game, true); + if (found) { + game.informPlayers(sourceObject.getLogName() + ": Found a creature that shares a creature type with the revealed card."); + if (controller.chooseUse(Outcome.Benefit, "Cast the card?", game)) { + controller.cast(card.getSpellAbility(), game, true); } else { - game.informPlayers("DescendantsPath: " + player.getLogName() + " canceled casting the card."); - player.getLibrary().putOnBottom(card, game); + game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " canceled casting the card."); + controller.getLibrary().putOnBottom(card, game); } } else { - game.informPlayers("DescendantsPath: No creature that shares a creature type with the revealed card."); - player.getLibrary().putOnBottom(card, game); + game.informPlayers(sourceObject.getLogName() + ": No creature that shares a creature type with the revealed card."); + controller.getLibrary().putOnBottom(card, game); } } else { - game.informPlayers("DescendantsPath: put " + card.getName() + " on the bottom."); - player.getLibrary().putOnBottom(card, game); + game.informPlayers(sourceObject.getLogName() + ": Put " + card.getLogName() + " on the bottom."); + controller.getLibrary().putOnBottom(card, game); } return true; From 9f7ce41679127609f7a1c7ae6b4036572db98601 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 17 Jun 2015 21:45:06 +0200 Subject: [PATCH 33/33] Some minor changes. --- .../src/mage/sets/commander/Fertilid.java | 53 +------- .../sets/dissension/PainMagnification.java | 26 ++-- .../src/mage/sets/lorwyn/AquitectsWill.java | 27 ++-- .../BecomesBasicLandTargetEffect.java | 26 ++-- ...rchLibraryPutInPlayTargetPlayerEffect.java | 119 ++++++++++++++++++ 5 files changed, 168 insertions(+), 83 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java diff --git a/Mage.Sets/src/mage/sets/commander/Fertilid.java b/Mage.Sets/src/mage/sets/commander/Fertilid.java index fd4d14ce21a..c0b3132f69c 100644 --- a/Mage.Sets/src/mage/sets/commander/Fertilid.java +++ b/Mage.Sets/src/mage/sets/commander/Fertilid.java @@ -35,16 +35,13 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; -import mage.cards.Card; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayTargetPlayerEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterBasicLandCard; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; @@ -63,8 +60,10 @@ public class Fertilid extends CardImpl { // Fertilid enters the battlefield with two +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), "with two +1/+1 counters on it")); + // {1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches his or her library for a basic land card and puts it onto the battlefield tapped. Then that player shuffles his or her library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new FertilidEffect(new TargetCardInLibrary(new FilterBasicLandCard()), true, true), new ManaCostsImpl("{1}{G}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new SearchLibraryPutInPlayTargetPlayerEffect(new TargetCardInLibrary(new FilterBasicLandCard()), true, true), new ManaCostsImpl("{1}{G}")); ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); @@ -79,47 +78,3 @@ public class Fertilid extends CardImpl { return new Fertilid(this); } } - -class FertilidEffect extends SearchLibraryPutInPlayEffect { - - public FertilidEffect(TargetCardInLibrary target, boolean tapped, boolean forceShuffle) { - super(target, tapped, forceShuffle); - - this.staticText = "Target player searches his or her library for a basic land card and puts it onto the battlefield tapped. Then that player shuffles his or her library."; - } - - public FertilidEffect(final FertilidEffect effect) { - super(effect); - } - - @Override - public FertilidEffect copy() { - return new FertilidEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - - if (player != null) { - if (player.searchLibrary(target, game)) { - if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().getCard(cardId, game); - if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), tapped); - } - } - } - player.shuffleLibrary(game); - return true; - } - - if (forceShuffle) { - player.shuffleLibrary(game); - } - } - - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/dissension/PainMagnification.java b/Mage.Sets/src/mage/sets/dissension/PainMagnification.java index bed43711a4f..418356ea74f 100644 --- a/Mage.Sets/src/mage/sets/dissension/PainMagnification.java +++ b/Mage.Sets/src/mage/sets/dissension/PainMagnification.java @@ -77,21 +77,23 @@ class PainMagnificationTriggeredAbility extends TriggeredAbilityImpl { public PainMagnificationTriggeredAbility copy() { return new PainMagnificationTriggeredAbility(this); } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - // If the damaged player is an opponent - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - int amount = event.getAmount(); - if(amount >= 3) { - // If at least 3 damage is dealt, set the opponent as the Discard target - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; - } - } + // If the damaged player is an opponent + if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { + if(event.getAmount() >= 3) { + // If at least 3 damage is dealt, set the opponent as the Discard target + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + } + return true; + } } return false; } diff --git a/Mage.Sets/src/mage/sets/lorwyn/AquitectsWill.java b/Mage.Sets/src/mage/sets/lorwyn/AquitectsWill.java index b78e153907e..a92d4b2eaa9 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/AquitectsWill.java +++ b/Mage.Sets/src/mage/sets/lorwyn/AquitectsWill.java @@ -37,7 +37,9 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; @@ -49,22 +51,20 @@ import mage.target.common.TargetLandPermanent; * @author ilcartographer */ public class AquitectsWill extends CardImpl { - private static final String rule = "land is an Island in addition to its other types for as long as it has a flood counter on it."; public AquitectsWill(UUID ownerId) { super(ownerId, 52, "Aquitect's Will", Rarity.COMMON, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{U}"); this.expansionSetCode = "LRW"; this.subtype.add("Merfolk"); - // Put a flood counter on target land. That land is an Island in addition to its other types for as long as it has a flood counter on it. If you control a Merfolk, draw a card. - // Add the flood counter to target land + // Put a flood counter on target land. this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.FLOOD.createInstance())); this.getSpellAbility().addTarget(new TargetLandPermanent()); - // Add the Flood counter effect + // That land is an Island in addition to its other types for as long as it has a flood counter on it. this.getSpellAbility().addEffect(new AquitectsWillEffect(Duration.Custom, false, false, "Island")); - // Draw if you control a Merfolk + // If you control a Merfolk, draw a card. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(1), new PermanentsOnTheBattlefieldCondition(new FilterControlledCreaturePermanent("Merfolk", "Merfolk")), @@ -85,6 +85,7 @@ class AquitectsWillEffect extends BecomesBasicLandTargetEffect { public AquitectsWillEffect(Duration duration, boolean chooseLandType, boolean loseType, String... landNames) { super(duration, chooseLandType, loseType, landNames); + staticText = "That land is an Island in addition to its other types for as long as it has a flood counter on it"; } public AquitectsWillEffect(final AquitectsWillEffect effect) { @@ -92,18 +93,20 @@ class AquitectsWillEffect extends BecomesBasicLandTargetEffect { } @Override - public boolean isInactive(Ability source, Game game) { + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (land != null && land.getCounters().getCount(CounterType.FLOOD) < 1) { + if (land == null) { + // if permanent left battlefield the effect can be removed because it was only valid for that object this.discard(); - return true; - } - - return false; + } else if (land.getCounters().getCount(CounterType.FLOOD) > 0) { + // only if Flood counter is on the object it becomes an Island.(it would be possible to remove and return the counters with e.g. Fate Transfer if the land becomes a creature too) + super.apply(layer, sublayer, source, game); + } + return true; } @Override public AquitectsWillEffect copy() { return new AquitectsWillEffect(this); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index 3cd7a1f11cd..39316525853 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java @@ -135,16 +135,22 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { case AbilityAddingRemovingEffects_6: land.removeAllAbilities(source.getSourceId(), game); for (String landType : landTypes) { - if (landType.equals("Swamp")) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } else if (landType.equals("Mountain")) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } else if (landType.equals("Forest")) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } else if (landType.equals("Island")) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } else if (landType.equals("Plains")) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + switch (landType) { + case "Swamp": + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + break; + case "Mountain": + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + case "Forest": + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + break; + case "Island": + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + case "Plains": + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; } } break; diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java new file mode 100644 index 00000000000..74cf05fbe9f --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java @@ -0,0 +1,119 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.search; + +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.SearchEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author LevelX2 + */ + +public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { + + protected boolean tapped; + protected boolean forceShuffle; + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target) { + this(target, false, true, Outcome.PutCardInPlay); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped) { + this(target, tapped, true, Outcome.PutCardInPlay); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped, boolean forceShuffle) { + this(target, tapped, forceShuffle, Outcome.PutCardInPlay); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped, Outcome outcome) { + this(target, tapped, true, outcome); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped, boolean forceShuffle, Outcome outcome) { + super(target, outcome); + this.tapped = tapped; + this.forceShuffle = forceShuffle; + setText(); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(final SearchLibraryPutInPlayTargetPlayerEffect effect) { + super(effect); + this.tapped = effect.tapped; + this.forceShuffle = effect.forceShuffle; + } + + @Override + public SearchLibraryPutInPlayTargetPlayerEffect copy() { + return new SearchLibraryPutInPlayTargetPlayerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (player != null) { + if (player.searchLibrary(target, game)) { + if (target.getTargets().size() > 0) { + for (UUID cardId: target.getTargets()) { + Card card = player.getLibrary().getCard(cardId, game); + if (card != null) { + player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), tapped); + } + } + } + player.shuffleLibrary(game); + return true; + } + + if (forceShuffle) { + player.shuffleLibrary(game); + } + } + + return false; + } + + private void setText() { + StringBuilder sb = new StringBuilder(); + sb.append("target player searches his or her library for "); + if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { + if ( target.getMaxNumberOfTargets() == Integer.MAX_VALUE ) { + sb.append("any number of ").append(" "); + } + else { + sb.append("up to ").append(target.getMaxNumberOfTargets()).append(" "); + } + sb.append(target.getTargetName()).append(" and put them onto the battlefield"); + } + else { + sb.append("a ").append(target.getTargetName()).append(" and put it onto the battlefield"); + } + if (tapped) { + sb.append(" tapped"); + } + if (forceShuffle) { + sb.append(". Then that player shuffles his or her library"); + } + else { + sb.append(". If that player does, he or she shuffles his or her library"); + } + staticText = sb.toString(); + } + + public List getTargets() { + return target.getTargets(); + } + +}