diff --git a/Mage.Client/release/sample-decks/2013/Modern/DailyMTG Reconstructed The Most Fun I've Had in Modern/Gavin Verhey's Nyxwave.dck b/Mage.Client/release/sample-decks/2013/Modern/DailyMTG Reconstructed The Most Fun I've Had in Modern/Gavin Verhey's Nyxwave.dck new file mode 100644 index 0000000000..659f546ad2 --- /dev/null +++ b/Mage.Client/release/sample-decks/2013/Modern/DailyMTG Reconstructed The Most Fun I've Had in Modern/Gavin Verhey's Nyxwave.dck @@ -0,0 +1,20 @@ +4 [M13:160] Arbor Elf +3 [MMA:144] Eternal Witness +3 [C13:148] Harmonize +4 [ROE:190] Joraga Treespeaker +1 [ZEN:220] Misty Rainforest +4 [SOM:122] Genesis Wave +1 [M14:169] Elvish Mystic +1 [GTC:247] Stomping Ground +1 [AVR:172] Craterhoof Behemoth +1 [ISD:243] Kessig Wolf Run +1 [FUT:177] Horizon Canopy +4 [THS:223] Nykthos, Shrine to Nyx +2 [EVE:74] Regal Force +10 [THS:246] Forest +4 [GTC:216] Burning-Tree Emissary +4 [DIS:99] Utopia Sprawl +4 [M12:188] Primeval Titan +4 [CMD:157] Garruk Wildspeaker +2 [M12:182] Llanowar Elves +2 [ZEN:229] Verdant Catacombs diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 0fd8ad9743..79b2344bfd 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -18,6 +18,7 @@ import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -227,11 +228,17 @@ public class ImageCache { BufferedImage image = getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL); TFile imageFile = new TFile(path); try { - //log.debug("thumbnail path:"+path); - TFileOutputStream outputStream = new TFileOutputStream(imageFile); - ImageIO.write(image, "jpg", outputStream); - outputStream.close(); - } catch (Exception e) { + TFileOutputStream outputStream = null; + try { + //log.debug("thumbnail path:"+path); + outputStream = new TFileOutputStream(imageFile); + ImageIO.write(image, "jpg", outputStream); + } finally { + if (outputStream != null) { + outputStream.close(); + } + } + } catch (IOException e) { log.error(e,e); } return image; @@ -239,6 +246,7 @@ public class ImageCache { /** * Returns an image scaled to the size given + * @return */ public static BufferedImage getNormalSizeImage(BufferedImage original) { if (original == null) { diff --git a/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java b/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java new file mode 100644 index 0000000000..8f7d7cf7e8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander2013/TemptWithReflections.java @@ -0,0 +1,136 @@ +/* + * 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.commander2013; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +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.permanent.Permanent; +import mage.players.Player; +import mage.players.PlayerList; +import mage.sets.tokens.EmptyToken; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class TemptWithReflections extends CardImpl { + + public TemptWithReflections(UUID ownerId) { + super(ownerId, 60, "Tempt with Reflections", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{U}"); + this.expansionSetCode = "C13"; + + this.color.setBlue(true); + + // Tempting offer - Choose target creature you control. Put a token onto the battlefield that's a copy of that creature. Each opponent may put a token onto the battlefield that's a copy of that creature. For each opponent who does, put a token onto the battlefield that's a copy of that creature. + this.getSpellAbility().addEffect(new TemptWithReflectionsEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(true)); + } + + public TemptWithReflections(final TemptWithReflections card) { + super(card); + } + + @Override + public TemptWithReflections copy() { + return new TemptWithReflections(this); + } +} + +class TemptWithReflectionsEffect extends OneShotEffect { + + public TemptWithReflectionsEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Tempting offer - Choose target creature you control. Put a token onto the battlefield that's a copy of that creature. Each opponent may put a token onto the battlefield that's a copy of that creature. For each opponent who does, put a token onto the battlefield that's a copy of that creature"; + } + + public TemptWithReflectionsEffect(final TemptWithReflectionsEffect effect) { + super(effect); + } + + @Override + public TemptWithReflectionsEffect copy() { + return new TemptWithReflectionsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); + } + + if (permanent != null) { + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(permanent); + token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + + Set playersSaidYes = new HashSet(); + PlayerList playerList = game.getPlayerList().copy(); + playerList.setCurrent(game.getActivePlayerId()); + Player player = game.getPlayer(game.getActivePlayerId()); + do { + if (game.getOpponents(source.getControllerId()).contains(player.getId())) { + String decision; + if (player.chooseUse(outcome, "Put a copy of target creature onto the battlefield for you?", game)) { + playersSaidYes.add(player.getId()); + decision = " chooses to copy "; + } else { + decision = " won't copy "; + } + game.informPlayers((new StringBuilder(player.getName()).append(decision).append(permanent.getName()).toString())); + } + player = playerList.getNext(game); + } while (!player.getId().equals(game.getActivePlayerId())); + + for (UUID playerId: playersSaidYes) { + token = new EmptyToken(); + CardUtil.copyTo(token).from(permanent); + token.putOntoBattlefield(1, game, source.getSourceId(), playerId); + } + + if (playersSaidYes.size() > 0) { + token = new EmptyToken(); + CardUtil.copyTo(token).from(permanent); + token.putOntoBattlefield(playersSaidYes.size(), game, source.getSourceId(), source.getControllerId()); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/commander2013/WarCadence.java b/Mage.Sets/src/mage/sets/commander2013/WarCadence.java index 4d02cf1325..6a0a3117d0 100644 --- a/Mage.Sets/src/mage/sets/commander2013/WarCadence.java +++ b/Mage.Sets/src/mage/sets/commander2013/WarCadence.java @@ -30,7 +30,6 @@ package mage.sets.commander2013; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; @@ -112,10 +111,7 @@ class WarCadenceReplacementEffect extends ReplacementEffectImpl= 20){ + Permanent permanent = game.getPermanent(this.getSourceId()); + if(permanent != null && permanent.getCounters().getCount(CounterType.CHARGE) >= 20){ return true; } return false; diff --git a/Mage.Sets/src/mage/sets/innistrad/MoldgrafMonstrosity.java b/Mage.Sets/src/mage/sets/innistrad/MoldgrafMonstrosity.java index 637e2c977f..704bd28a53 100644 --- a/Mage.Sets/src/mage/sets/innistrad/MoldgrafMonstrosity.java +++ b/Mage.Sets/src/mage/sets/innistrad/MoldgrafMonstrosity.java @@ -113,6 +113,9 @@ class MoldgrafMonstrosityEffect extends OneShotEffect } private Card getRandomCard(Set cards) { + if (cards == null || cards.size() < 1) { + return null; + } int i = 0; int pick = new Random().nextInt(cards.size()); for (Card card : cards) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java b/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java index 562c24ffb8..d957c6bdf4 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/JaceArchitectOfThought.java @@ -66,6 +66,7 @@ import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** @@ -359,16 +360,19 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect 0 && controller.choose(Outcome.PlayForFree, JaceExileZone, target, game)) { + while (jaceExileZone.count(filter, game) > 0 && controller.choose(Outcome.PlayForFree, jaceExileZone, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java b/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java index 2b251929cb..386d42a91d 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java @@ -110,7 +110,9 @@ class TwilightShepherdEffect extends OneShotEffect { Set cardsInGraveyardId = watcher.getCardsPutToGraveyardFromBattlefield(); for (UUID cardId : cardsInGraveyardId) { Card card = game.getCard(cardId); - if (card != null && card.getOwnerId().equals(source.getControllerId())) { + if (card != null + && card.getOwnerId().equals(source.getControllerId()) + && game.getState().getZone(card.getId()).match(Zone.GRAVEYARD)) { applied = card.moveToZone(Zone.HAND, source.getSourceId(), game, false); } } diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml index 621061ddc9..56062bf06a 100644 --- a/Mage.Tests/pom.xml +++ b/Mage.Tests/pom.xml @@ -61,23 +61,27 @@ - + + - org.apache.maven.plugins - maven-compiler-plugin - - 1.6 - 1.6 - + org.apache.maven.plugins + maven-surefire-plugin + 2.16 - maven-resources-plugin - - UTF-8 - + org.apache.maven.plugins + maven-compiler-plugin + 3.1 - - + + maven-resources-plugin + 2.6 + + UTF-8 + + + + mage-tests diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java new file mode 100644 index 0000000000..b23a5819a1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -0,0 +1,69 @@ +/* + * 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 org.mage.test.cards.abilities.keywords; + +import mage.abilities.keyword.HexproofAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class HexproofTest extends CardTestPlayerBase { + + /** + * Tests one target gets hexproof + */ + @Test + public void testOneTargetOneGainingHexproof() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Elder of Laurels"); + addCard(Zone.HAND, playerA, "Ranger's Guile"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + addCard(Zone.HAND, playerB, "Into the Void"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Into the Void", "Elder of Laurels"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Ranger's Guile", "Elder of Laurels"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + // because of hexproof the Elder should be onto the battlefield + assertPermanentCount(playerA, "Elder of Laurels", 1); + assertPowerToughness(playerA, "Elder of Laurels", 3, 4); + assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); + } + /** + * Tests one target gets hexproof + */ + @Test + public void testTwoTargetsOneGainingHexproof() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Elder of Laurels"); + addCard(Zone.BATTLEFIELD, playerA, "Arbor Elf"); + addCard(Zone.HAND, playerA, "Ranger's Guile"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + addCard(Zone.HAND, playerB, "Into the Void"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Into the Void", "Elder of Laurels^Arbor Elf"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Ranger's Guile", "Elder of Laurels"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + // because of hexproof the Elder should be onto the battlefield + assertPermanentCount(playerA, "Elder of Laurels", 1); + assertPowerToughness(playerA, "Elder of Laurels", 3, 4); + assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); + assertPermanentCount(playerA, "Arbor Elf", 0); + } +} diff --git a/Mage/src/mage/target/targetpointer/FirstTargetPointer.java b/Mage/src/mage/target/targetpointer/FirstTargetPointer.java index 04d2d24e00..040526ff1c 100644 --- a/Mage/src/mage/target/targetpointer/FirstTargetPointer.java +++ b/Mage/src/mage/target/targetpointer/FirstTargetPointer.java @@ -1,10 +1,14 @@ package mage.target.targetpointer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; import mage.game.Game; - -import java.util.*; +import mage.target.Target; public class FirstTargetPointer implements TargetPointer { @@ -40,12 +44,16 @@ public class FirstTargetPointer implements TargetPointer { public List getTargets(Game game, Ability source) { ArrayList target = new ArrayList(); if (source.getTargets().size() > 0) { + Target currentTarget = source.getTargets().get(0); for (UUID targetId : source.getTargets().get(0).getTargets()) { Card card = game.getCard(targetId); if (card != null && zoneChangeCounter.containsKey(targetId) && card.getZoneChangeCounter() != zoneChangeCounter.get(targetId)) { continue; } + if (!currentTarget.canTarget(targetId, source, game)) { + continue; + } target.add(targetId); } } diff --git a/Mage/src/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/mage/target/targetpointer/SecondTargetPointer.java index 680e324c1d..3cc0a54daa 100644 --- a/Mage/src/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/mage/target/targetpointer/SecondTargetPointer.java @@ -1,11 +1,14 @@ package mage.target.targetpointer; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; import mage.game.Game; - public class SecondTargetPointer implements TargetPointer { private Map zoneChangeCounter = new HashMap(); diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index 03b95bba39..756d65f1d5 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -18,6 +18,8 @@ git log 68333a2eff6b643b2028d18dad16d1f228be7a2c..HEAD --diff-filter=A --name-st git log 10902581140fe4268fc12408f099ad82347d7cd0..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt since 1.1.0-release: git log d6c1075125e657d4dd2e7bb120e108bb4c4536ff..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.1.2-2013_10_26-release: +git log 63889f5bd4faa0a0915bb1e845ca3a0bc1093070..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: