From 5ea17241123c67ed240158356335999ae40b040f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 00:33:01 +0100 Subject: [PATCH 01/18] * Ghirapur Guide - Fixed that the effect of the activated ability lasted while Ghirapur Guide was on the battlefield instead only until end of turn. --- .../effects/common/combat/CantBeBlockedByAllTargetEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java index 34d6d52a5e..4d1136e4b4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java @@ -21,7 +21,7 @@ public class CantBeBlockedByAllTargetEffect extends RestrictionEffect { private final FilterCreaturePermanent filterBlockedBy; public CantBeBlockedByAllTargetEffect(FilterCreaturePermanent filterBlockedBy, Duration duration) { - super(Duration.WhileOnBattlefield); + super(duration); this.filterBlockedBy = filterBlockedBy; staticText = "Target creature" + " can't be blocked " From 4018d39dabc1f1267f28543fe7ca6a46033b448a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 00:45:18 +0100 Subject: [PATCH 02/18] Added test. --- .../cards/enchantments/AnimateDeadTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java index f675596a86..ee77d60ed7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java @@ -121,4 +121,37 @@ public class AnimateDeadTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Animate Dead", 1); assertPermanentCount(playerA, "Animate Dead", 0); } + + /** + * Animate Dead is incorrectly not entering the graveyard when the animated + * target is sacrificed. + */ + @Test + public void testAnimateAndSacrificeTarget() { + // Target opponent sacrifices a creature. + addCard(Zone.HAND, playerB, "Cruel Edict"); // {1}{B} + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Enchant creature card in a graveyard + // 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. + // Enchanted creature gets -1/-0. + addCard(Zone.HAND, playerA, "Animate Dead"); // {1}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Silvercoat Lion"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cruel Edict", playerA); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Cruel Edict", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertGraveyardCount(playerA, "Animate Dead", 1); + } + } From 7d60d884c37419b298154501ca286bc005de2d6d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 01:15:45 +0100 Subject: [PATCH 03/18] * Refelecting Pool - Fixed that mana that Exotic Orchard could produce was not taken into account. --- .../test/cards/mana/ReflectingPoolTest.java | 21 +++++++++++++++++++ .../mana/AnyColorLandsProduceManaAbility.java | 8 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java index 7ab29a93f0..b55de2a10f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java @@ -27,8 +27,10 @@ */ package org.mage.test.cards.mana; +import mage.abilities.mana.ManaOptions; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -68,4 +70,23 @@ public class ReflectingPoolTest extends CardTestPlayerBase { } + /** + * Reflecting Pool does not see what mana Exotic Orchard can produce + */ + @Test + public void testWithExoticOrchard() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + // {T}: Add to your mana pool one mana of any type that a land you control could produce. + addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); + // {T}: Add to your mana pool one mana of any color that a land an opponent controls could produce. + addCard(Zone.BATTLEFIELD, playerA, "Exotic Orchard", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions options = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("Player should be able to create 2 red mana", "{R}{R}", options.get(0).toString()); + + } } diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java index 398c1f2741..989b558f60 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java @@ -69,6 +69,12 @@ public class AnyColorLandsProduceManaAbility extends ActivatedManaAbilityImpl { public List getNetMana(Game game) { return ((AnyColorLandsProduceManaEffect) getEffects().get(0)).getNetMana(game, this); } + + @Override + public boolean definesMana() { + return true; + } + } class AnyColorLandsProduceManaEffect extends ManaEffect { @@ -155,7 +161,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { } private Mana getManaTypes(Game game, Ability source) { - List lands = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); + List lands = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); Mana types = new Mana(); for (Permanent land : lands) { Abilities mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); From 72f2382199373d6c6d202f3e4675cb46b51f7ce3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 09:23:21 +0100 Subject: [PATCH 04/18] * Gravespawn Sovereign - Fixed tool tip rule text. --- Mage.Sets/src/mage/cards/g/GravespawnSovereign.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GravespawnSovereign.java b/Mage.Sets/src/mage/cards/g/GravespawnSovereign.java index e2a9e3cd95..73ec30086c 100644 --- a/Mage.Sets/src/mage/cards/g/GravespawnSovereign.java +++ b/Mage.Sets/src/mage/cards/g/GravespawnSovereign.java @@ -50,8 +50,8 @@ import mage.target.common.TargetControlledCreaturePermanent; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class GravespawnSovereign extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Vampires you control"); + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Zombies you control"); static { filter.add(new SubtypePredicate("Zombie")); @@ -59,7 +59,7 @@ public class GravespawnSovereign extends CardImpl { } public GravespawnSovereign(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); this.subtype.add("Zombie"); this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -68,7 +68,7 @@ public class GravespawnSovereign extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(5, 5, filter, true))); ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); this.addAbility(ability); - + } public GravespawnSovereign(final GravespawnSovereign card) { From 5aa1d1e4227f76aaef67990fadd6ab747dc113ed Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 09:23:34 +0100 Subject: [PATCH 05/18] * Demonspine Whip - Fixed tool tip rule text. --- Mage.Sets/src/mage/cards/d/DemonspineWhip.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DemonspineWhip.java b/Mage.Sets/src/mage/cards/d/DemonspineWhip.java index cb54ac9894..a10e00bf47 100644 --- a/Mage.Sets/src/mage/cards/d/DemonspineWhip.java +++ b/Mage.Sets/src/mage/cards/d/DemonspineWhip.java @@ -27,6 +27,7 @@ */ package mage.cards.d; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -41,8 +42,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import java.util.UUID; - /** * * @author jeffwadsworth @@ -50,12 +49,9 @@ import java.util.UUID; public class DemonspineWhip extends CardImpl { public DemonspineWhip(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{B}{R}"); this.subtype.add("Equipment"); - - - // {X}: Equipped creature gets +X/+0 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(new XPaid(), new StaticValue(0), Duration.EndOfTurn), new ManaCostsImpl("{X}"))); @@ -88,7 +84,7 @@ class XPaid implements DynamicValue { @Override public String getMessage() { - return "X paid"; + return ""; } @Override From ee5376c896c0cabdd034625a95f1a033739b21d2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 09:26:48 +0100 Subject: [PATCH 06/18] * Nature's Way - Fixed that trample was not applied to target creature. --- Mage.Sets/src/mage/cards/n/NaturesWay.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/n/NaturesWay.java b/Mage.Sets/src/mage/cards/n/NaturesWay.java index 92fecc3215..4a8212e290 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesWay.java +++ b/Mage.Sets/src/mage/cards/n/NaturesWay.java @@ -61,7 +61,7 @@ public class NaturesWay extends CardImpl { } public NaturesWay(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Target creature you control gains vigilance and trample until end of turn. It deals damage equal to its power to target creature you don't control. Effect effect = new GainAbilityTargetEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn); @@ -69,6 +69,7 @@ public class NaturesWay extends CardImpl { this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("and trample until end of turn"); + this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new NaturesWayEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); From d3e6ae6bcdbe678192e175f626e21ed7ccb96959 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 10:12:21 +0100 Subject: [PATCH 07/18] * Fixed test. --- .../test/cards/triggers/dies/ReturnOnlyFromGraveyardTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ReturnOnlyFromGraveyardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ReturnOnlyFromGraveyardTest.java index cc8bb46cf7..fba692a07d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ReturnOnlyFromGraveyardTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ReturnOnlyFromGraveyardTest.java @@ -61,6 +61,7 @@ public class ReturnOnlyFromGraveyardTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Academy Rector"); + setChoice(playerA, "When enchanted creature dies"); // Select triggered ability to execute last setChoice(playerA, "Yes"); addTarget(playerA, "Primal Rage"); @@ -94,7 +95,8 @@ public class ReturnOnlyFromGraveyardTest extends CardTestPlayerBase { castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Lightning Bolt", "Academy Rector"); - setChoice(playerA, "Yes"); + setChoice(playerA, "When enchanted creature dies"); // Select triggered ability to execute last + setChoice(playerA, "Yes"); // May exile it addTarget(playerA, "Primal Rage"); setStopAt(1, PhaseStep.END_COMBAT); From da7982ff2ed03ebc256d8506b956b034c6dcc1dd Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 10:13:04 +0100 Subject: [PATCH 08/18] * Obscuring Aether - Fixed a problem that face down creatures had P/T = 0/0. --- Mage.Sets/src/mage/cards/o/ObscuringAether.java | 4 ++-- .../common/continuous/BecomesFaceDownCreatureEffect.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/ObscuringAether.java b/Mage.Sets/src/mage/cards/o/ObscuringAether.java index 8e514390f4..5cbc3d93ee 100644 --- a/Mage.Sets/src/mage/cards/o/ObscuringAether.java +++ b/Mage.Sets/src/mage/cards/o/ObscuringAether.java @@ -55,14 +55,14 @@ public class ObscuringAether extends CardImpl { } public ObscuringAether(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // Face-down creature spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); // {1}{G}: Turn Obscuring Aether face down. Effect effect = new BecomesFaceDownCreatureEffect(Duration.Custom, BecomesFaceDownCreatureEffect.FaceDownType.MANUAL); - effect.setText("Turn Obscuring Aether face down. (It becomes a 2/2 creature.)"); + effect.setText("Turn {this} face down. (It becomes a 2/2 creature.)"); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{G}"))); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 0afee43a10..43d7ce2faa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -196,8 +196,8 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen break; case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { -// permanent.getPower().setValue(2); -// permanent.getToughness().setValue(2); + permanent.getPower().setValue(2); + permanent.getToughness().setValue(2); } } } else if (duration.equals(Duration.Custom) && foundPermanent == true) { From e6afdf087476301683b0aa0d6752537acc9d4e05 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 10:15:11 +0100 Subject: [PATCH 09/18] * Obscuring Aether - Fixed a problem that face down creatures had P/T = 0/0. --- .../cards/facedown/ObscuringAetherTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/facedown/ObscuringAetherTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/ObscuringAetherTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/ObscuringAetherTest.java new file mode 100644 index 0000000000..471ad8beba --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/ObscuringAetherTest.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 org.mage.test.cards.facedown; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ObscuringAetherTest extends CardTestPlayerBase { + + /** + * Obscuring Aether cannot turn into a face down 2/2 like it should. When + * activating the ability to turn it over it, it dies immediately. + * + */ + // test that cards exiled using Ghastly Conscription return face down + @Test + public void testTurnFaceDown() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // Face-down creature spells you cast cost {1} less to cast. + // {1}{G}: Turn Obscuring Aether face down. + addCard(Zone.HAND, playerA, "Obscuring Aether"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Obscuring Aether"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{G}: Turn"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, "Obscuring Aether", 0); + assertGraveyardCount(playerA, "Obscuring Aether", 0); + + assertPermanentCount(playerA, "", 1); + assertPowerToughness(playerA, "", 2, 2); + + } + +} From 2d43f4fa0ed11ad3e18b1de19080f461ca4f8f71 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 10:25:56 +0100 Subject: [PATCH 10/18] * Collective Restraint - Fixed that it also wrongly prevented players from attacking planeswalkers without paying the cost. --- Mage.Sets/src/mage/cards/c/CollectiveRestraint.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CollectiveRestraint.java b/Mage.Sets/src/mage/cards/c/CollectiveRestraint.java index e51144f6a8..d0cf351fc6 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveRestraint.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveRestraint.java @@ -28,7 +28,6 @@ package mage.cards.c; import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCosts; @@ -49,7 +48,7 @@ import mage.game.events.GameEvent; public class CollectiveRestraint extends CardImpl { public CollectiveRestraint(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); // Domain - Creatures can't attack you unless their controller pays {X} for each creature he or she controls that's attacking you, where X is the number of basic land types you control. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CollectiveRestraintPayManaToAttackAllEffect())); @@ -69,7 +68,7 @@ public class CollectiveRestraint extends CardImpl { class CollectiveRestraintPayManaToAttackAllEffect extends CantAttackYouUnlessPayManaAllEffect { CollectiveRestraintPayManaToAttackAllEffect() { - super(null, true); + super(null, false); staticText = "Creatures can't attack you unless their controller pays {X} for each creature he or she controls that's attacking you, where X is the number of basic land types you control."; } From 7fd2eeedcf4bafc8af775730f46d0ef9b83b49ad Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 20 Nov 2016 23:13:16 +1100 Subject: [PATCH 11/18] Add 'EDH power level' rating to Commander tables --- .../mage/client/dialog/NewTableDialog.form | 13 + .../mage/client/dialog/NewTableDialog.java | 19 +- .../src/mage/deck/Commander.java | 270 ++++++++++++++++++ .../java/mage/server/TableController.java | 13 + .../java/mage/cards/decks/DeckValidator.java | 4 + .../java/mage/game/match/MatchOptions.java | 9 + 6 files changed, 326 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index 8a9d385eae..6f93568ea0 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -59,6 +59,10 @@ + + + + @@ -133,6 +137,8 @@ + + @@ -389,5 +395,12 @@ + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 2e41a98513..95158581c3 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -79,6 +79,7 @@ public class NewTableDialog extends MageDialog { this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1)); this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1)); this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5)); + this.spnEdhPowerLevel.setModel(new SpinnerNumberModel(100, 0, 100, 5)); MageFrame.getUI().addButton(MageComponents.NEW_TABLE_OK_BUTTON, btnOK); } @@ -125,7 +126,9 @@ public class NewTableDialog extends MageDialog { btnOK = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); lblQuitRatio = new javax.swing.JLabel(); + lblEdhPowerLevel = new javax.swing.JLabel(); spnQuitRatio = new javax.swing.JSpinner(); + spnEdhPowerLevel = new javax.swing.JSpinner(); setTitle("New Table"); @@ -214,8 +217,10 @@ public class NewTableDialog extends MageDialog { }); lblQuitRatio.setText("Allowed quit %"); + lblEdhPowerLevel.setText("EDH power level"); spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table"); + spnEdhPowerLevel.setToolTipText("Players with decks with a higher power level can't join this table"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -256,7 +261,10 @@ public class NewTableDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(lblQuitRatio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblEdhPowerLevel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -314,7 +322,10 @@ public class NewTableDialog extends MageDialog { .addComponent(lbDeckType) .addComponent(lblQuitRatio) .addComponent(chkRated) - .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblEdhPowerLevel) + .addComponent(chkRated) + .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -395,6 +406,7 @@ public class NewTableDialog extends MageDialog { options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); options.setQuitRatio((Integer) this.spnQuitRatio.getValue()); + options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue()); if (!checkMatchOptions(options)) { return; } @@ -658,6 +670,7 @@ public class NewTableDialog extends MageDialog { } this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO, "100"))); + this.spnEdhPowerLevel.setValue(0); } /** @@ -721,6 +734,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JLabel lblNumWins; private javax.swing.JLabel lblPassword; private javax.swing.JLabel lblQuitRatio; + private javax.swing.JLabel lblEdhPowerLevel; private javax.swing.JLabel lblRange; private javax.swing.JLabel lblSkillLevel; private mage.client.table.NewPlayerPanel player1Panel; @@ -729,6 +743,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JSpinner spnNumPlayers; private javax.swing.JSpinner spnNumWins; private javax.swing.JSpinner spnQuitRatio; + private javax.swing.JSpinner spnEdhPowerLevel; private javax.swing.JTextField txtName; private javax.swing.JTextField txtPassword; // End of variables declaration//GEN-END:variables diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 6fef4d52ed..11fa3fbc50 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -204,4 +204,274 @@ public class Commander extends Constructed { || cardColor.isWhite() && !commander.isWhite()); } + @Override + public int getEdhPowerLevel(Deck deck) { + if (deck == null) { + return 0; + } + + int edhPowerLevel = 0; + for (Card card : deck.getCards()) { + + int thisMaxPower = 0; + + // Examine rules to work out most egregious functions in edh + boolean anyNumberOfTarget = false; + boolean buyback = false; + boolean cascade = false; + boolean copy = false; + boolean exile = false; + boolean exileAll = false; + boolean counter = false; + boolean destroy = false; + boolean destroyAll = false; + boolean each = false; + boolean exalted = false; + boolean drawCards = false; + boolean extraTurns = false; + boolean gainControl = false; + boolean infect = false; + boolean mayCastForFree = false; + boolean overload = false; + boolean persist = false; + boolean proliferate = false; + boolean retrace = false; + boolean sacrifice = false; + boolean skip = false; + boolean sliver = false; + boolean tutor = false; + boolean undying = false; + boolean wheneverEnters = false; + boolean youControlTarget = false; + + for (String str : card.getRules()) { + String s = str.toLowerCase(); + anyNumberOfTarget |= s.contains("any number"); + buyback |= s.contains("buyback"); + cascade |= s.contains("cascade"); + copy |= s.contains("copy"); + counter |= s.contains("counter") && s.contains("target"); + destroy |= s.contains("destroy"); + destroyAll |= s.contains("destroy all"); + drawCards |= s.contains("draw cards"); + each |= s.contains("each"); + exalted |= s.contains("exalted"); + exile |= s.contains("exile"); + exileAll |= s.contains("exile") && s.contains(" all "); + extraTurns |= s.contains("extra turn"); + gainControl |= s.contains("gain control"); + infect |= s.contains("infect"); + mayCastForFree |= s.contains("may cast") && s.contains("without paying"); + overload |= s.contains("overload"); + persist |= s.contains("persist"); + proliferate |= s.contains("proliferate"); + retrace |= s.contains("retrace"); + sacrifice |= s.contains("sacrifice"); + skip |= s.contains("skip") && s.contains("each"); + sliver |= s.contains("sliver"); + tutor |= s.contains("search your library"); + undying |= s.contains("undying"); + wheneverEnters |= s.contains("when") && s.contains("another") && s.contains("enters"); + youControlTarget |= s.contains("you control target"); + } + + if (extraTurns) { + thisMaxPower = Math.max(thisMaxPower, 7); + } + if (buyback) { + thisMaxPower = Math.max(thisMaxPower, 6); + } + if (tutor) { + thisMaxPower = Math.max(thisMaxPower, 6); + } + if (infect) { + thisMaxPower = Math.max(thisMaxPower, 5); + } + if (overload) { + thisMaxPower = Math.max(thisMaxPower, 5); + } + if (cascade) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (each) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (exileAll) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (gainControl) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (mayCastForFree) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (proliferate) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (skip) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (wheneverEnters) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (youControlTarget) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + if (anyNumberOfTarget) { + thisMaxPower = Math.max(thisMaxPower, 3); + } + if (destroyAll) { + thisMaxPower = Math.max(thisMaxPower, 3); + } + if (undying) { + thisMaxPower = Math.max(thisMaxPower, 3); + } + if (persist) { + thisMaxPower = Math.max(thisMaxPower, 3); + } + if (exile) { + thisMaxPower = Math.max(thisMaxPower, 2); + } + if (sliver) { + thisMaxPower = Math.max(thisMaxPower, 2); + } + if (sacrifice) { + thisMaxPower = Math.max(thisMaxPower, 2); + } + if (copy) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + if (counter) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + if (destroy) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + if (drawCards) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + if (exalted) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + if (retrace) { + thisMaxPower = Math.max(thisMaxPower, 1); + } + + // Planeswalkers + if (card.getCardType().contains(CardType.PLANESWALKER)) { + if (card.getName().toLowerCase().equals("jace, the mind sculptor")) { + thisMaxPower = Math.max(thisMaxPower, 5); + } + thisMaxPower = Math.max(thisMaxPower, 3); + } + + if (card.getCardType().contains(CardType.LAND)) { + thisMaxPower = 0; + } + + // Banned in french + String cn = card.getName().toLowerCase(); + if (cn.equals("ancient tomb") || cn.equals("armageddon") + || cn.equals("aura shards") || cn.equals("back to basics") + || cn.equals("bane of progress") || cn.equals("basalt monolith") + || cn.equals("blightsteel collossus") || cn.equals("cabal coffers") + || cn.equals("craterhoof behemoth") || cn.equals("deepglow skate") + || cn.equals("dig through time") || cn.equals("entomb") + || cn.equals("food chain") || cn.equals("gaea's cradle") + || cn.equals("grim monolith") || cn.equals("hermit druid") + || cn.equals("humility") || cn.equals("imperial seal") + || cn.equals("karakas") || cn.equals("living death") + || cn.equals("loyal retainers") || cn.equals("mana crypt") + || cn.equals("mana drain") || cn.equals("mana vault") + || cn.equals("necrotic ooze") || cn.equals("oath of druids") + || cn.equals("protean hulk") || cn.equals("ravages of war") + || cn.equals("reclamation sage") || cn.equals("sensei's divning top") + || cn.equals("sol ring") || cn.equals("spore frog") + || cn.equals("strip mine") || cn.equals("the tabernacle at pendrell vale") + || cn.equals("tinker") || cn.equals("tolarian academy") + || cn.equals("winter orb") || cn.equals("treasure cruise")) { + thisMaxPower = Math.max(thisMaxPower, 4); + } + + // Parts of infinite combos + if (cn.equals("animate artifact") || cn.equals("archaeomancer") + || cn.equals("ashnod's altar") || cn.equals("azami, lady of scrolls") + || cn.equals("basalt monolith") || cn.equals("brago, king eternal") + || cn.equals("candelabra of tawnos") || cn.equals("cephalid aristocrat") + || cn.equals("cephalid illusionist") || cn.equals("changeling berserker") + || cn.equals("cinderhaze wretch") || cn.equals("cryptic gateway") + || cn.equals("deadeye navigator") || cn.equals("derevi, empyrial tactician") + || cn.equals("doubling season") || cn.equals("dross scorpion") + || cn.equals("earthcraft") || cn.equals("erratic portal") + || cn.equals("enter the infinite") || cn.equals("omniscience") + || cn.equals("exquisite blood") || cn.equals("future sight") + || cn.equals("grave titan") || cn.equals("great whale") + || cn.equals("grim monolith") || cn.equals("gush") + || cn.equals("hellkite charger") || cn.equals("intruder alarm") + || cn.equals("iona, shield of emeria") + || cn.equals("karn, silver golem") || cn.equals("kiki-jiki, mirror breaker") + || cn.equals("krark-clan ironworks") || cn.equals("krenko, mob boss") + || cn.equals("krosan restorer") || cn.equals("laboratory maniac") + || cn.equals("leovold, emissary of trest") + || cn.equals("leonin relic-warder") || cn.equals("leyline of the void") + || cn.equals("memnarch") || cn.equals("memnarch") + || cn.equals("meren of clan nel toth") || cn.equals("mikaeus, the unhallowed") + || cn.equals("mindcrank") || cn.equals("mindslaver") + || cn.equals("minion reflector") || cn.equals("mycosynth lattice") + || cn.equals("myr turbine") || cn.equals("narset, enlightened master") + || cn.equals("nekusar, the mindrazer") || cn.equals("norin the wary") + || cn.equals("opalescence") || cn.equals("ornithopter") + || cn.equals("planar portal") || cn.equals("power artifact") + || cn.equals("rings of brighthearth") || cn.equals("rite of replication") + || cn.equals("sanguine bond") || cn.equals("sensei's divining top") + || cn.equals("splinter twin") || cn.equals("stony silence") + || cn.equals("storm cauldron") || cn.equals("teferi's puzzle box") + || cn.equals("teferi, mage of zhalfir") || cn.equals("teferi, mage of zhalfir") + || cn.equals("tezzeret the seeker") || cn.equals("time stretch") + || cn.equals("time warp") || cn.equals("training grounds") + || cn.equals("triskelavus") || cn.equals("triskelion") + || cn.equals("turnabout") || cn.equals("umbral mantle") + || cn.equals("uyo, silent prophet") || cn.equals("voltaic key") + || cn.equals("workhorse") || cn.equals("worldgorger dragon") + || cn.equals("worthy cause") || cn.equals("yawgmoth's will") + || cn.equals("zealous conscripts")) { + thisMaxPower = Math.max(thisMaxPower, 6); + } + System.out.println(thisMaxPower + "Card:" + cn + " " + thisMaxPower); + + edhPowerLevel += thisMaxPower; + } + + for (Card commander : deck.getSideboard()) { + int thisMaxPower = 0; + String cn = commander.getName().toLowerCase(); + // Least fun commanders + if (cn.equals("memnarch") + || cn.equals("derevi, empyrial tactician") + || cn.equals("narset, enlightened master") + || cn.equals("nekusar, the mindrazer") + || cn.equals("norin the wary")) { + thisMaxPower = Math.max(thisMaxPower, 15); + } + + // Next least fun commanders + if (cn.equals("meren of clan nel toth") + || cn.equals("teferi, mage of zhalfir") + || cn.equals("azami, lady of scrolls") + || cn.equals("brago, king eternal") + || cn.equals("mikaeus the unhallowed") + || cn.equals("memnarch")) { + thisMaxPower = Math.max(thisMaxPower, 10); + } + + System.out.println(thisMaxPower + "Card:" + cn + " bad commander" + thisMaxPower); + edhPowerLevel += thisMaxPower; + } + + edhPowerLevel = (int) Math.round(edhPowerLevel / 2.5); + if (edhPowerLevel > 100) { + edhPowerLevel = 100; + } + return edhPowerLevel; + } } diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 2162af3c6b..6b1704182f 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -297,6 +297,19 @@ public class TableController { user.showUserMessage("Join Table", message); return false; } + + // Check power level for table (currently only used for EDH/Commander table) + int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel(); + if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase().equals("commander")) { + int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck); + if (deckEdhPowerLevel > edhPowerLevel) { + String message = new StringBuilder("Your deck appears to be too powerful for this table.\n\nReduce the number of extra turn cards, infect, counters, fogs, reconsider your commander. ") + .append("\nThe table requirement has a maximum power level of ").append(edhPowerLevel).append (" whilst your deck has a calculated power level of ") + .append(deckEdhPowerLevel).toString(); + user.showUserMessage("Join Table", message); + return false; + } + } Player player = createPlayer(name, seat.getPlayerType(), skill); if (player == null) { diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidator.java b/Mage/src/main/java/mage/cards/decks/DeckValidator.java index 383e1ad8a0..bc73bc61d9 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckValidator.java +++ b/Mage/src/main/java/mage/cards/decks/DeckValidator.java @@ -67,4 +67,8 @@ public abstract class DeckValidator implements Serializable { } } } + + public int getEdhPowerLevel(Deck deck) { + return 0; + } } diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index 37901b91be..3bcd909ab8 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.java @@ -58,6 +58,7 @@ public class MatchOptions implements Serializable { protected SkillLevel skillLevel; protected boolean rollbackTurnsAllowed; protected int quitRatio; + protected int edhPowerLevel; protected boolean rated; protected int numSeatsForMatch; @@ -208,6 +209,14 @@ public class MatchOptions implements Serializable { public void setQuitRatio(int quitRatio) { this.quitRatio = quitRatio; } + + public int getEdhPowerLevel() { + return edhPowerLevel; + } + + public void setEdhPowerLevel(int edhPowerLevel) { + this.edhPowerLevel = edhPowerLevel; + } public boolean isRated() { return rated; From 7eb8d167b33e476de32df81a7580eeff00aaa094 Mon Sep 17 00:00:00 2001 From: Pete Rossi Date: Sun, 20 Nov 2016 11:10:02 -0800 Subject: [PATCH 12/18] Fix for Sanguine Praetor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sanguine Praetor’s activated ability is only destroying creatures its owner controls. This fix changes it to target all creatures. If my interpretation of the card text is incorrect then feel free to close this pull request. --- Mage.Sets/src/mage/cards/s/SanguinePraetor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java index e017643082..274ff1c8ba 100644 --- a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java +++ b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java @@ -98,7 +98,7 @@ class SanguinePraetorEffect extends OneShotEffect { } } - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game)) { if (permanent.getConvertedManaCost() == cmc) { permanent.destroy(source.getSourceId(), game, false); } From 7daa4765424a037b79787f99a4b08179b04d684b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 22:34:12 +0100 Subject: [PATCH 13/18] * Fixed that the color of cards cast face dwon and later turned face up was not set. --- .../abilities/keywords/ManifestTest.java | 4 +- .../abilities/keywords/MegamorphTest.java | 66 +++++++++++++++++++ .../src/main/java/mage/game/ZonesHandler.java | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MegamorphTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index cb724e8731..4d9ad42869 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -30,6 +30,7 @@ package org.mage.test.cards.abilities.keywords; import mage.cards.Card; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.permanent.Permanent; import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -214,7 +215,6 @@ public class ManifestTest extends CardTestPlayerBase { /* I casted a Silence the Believers on a manifested card. It moved to the exile zone face-down. */ - @Test public void testCardGetsExiledFaceUp() { addCard(Zone.BATTLEFIELD, playerB, "Island", 2); @@ -326,6 +326,8 @@ public class ManifestTest extends CardTestPlayerBase { assertPermanentCount(playerB, "", 0); assertPermanentCount(playerB, "Aerie Bowmasters", 1); assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph + Permanent aerie = getPermanent("Aerie Bowmasters", playerB); + Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MegamorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MegamorphTest.java new file mode 100644 index 0000000000..c70a595461 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MegamorphTest.java @@ -0,0 +1,66 @@ +/* + * 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.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class MegamorphTest extends CardTestPlayerBase { + + @Test + public void testManifestMegamorph() { + // Reach (This creature can block creatures with flying.) + // Megamorph {5}{G} + addCard(Zone.HAND, playerA, "Aerie Bowmasters", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aerie Bowmasters"); + setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}{G}: Turn"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Aerie Bowmasters", 1); + assertPowerToughness(playerA, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph + + Permanent aerie = getPermanent("Aerie Bowmasters", playerA); + Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); + + } + +} diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index f78a74e9ee..ede698ad58 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -288,7 +288,7 @@ public class ZonesHandler { private static Card takeAttributesFromSpell(Card card, ZoneChangeEvent event, Game game) { if (Zone.STACK.equals(event.getFromZone())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null) { + if (spell != null && !spell.isFaceDown(game)) { boolean doCopy = false; if (!card.getColor(game).equals(spell.getColor(game))) { doCopy = true; From 612445d71179b34f95f14d2b1314f9af7927477f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Nov 2016 22:48:08 +0100 Subject: [PATCH 14/18] * Fixed a problem of Sanguine Praetor. --- Mage.Sets/src/mage/cards/s/SanguinePraetor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java index 274ff1c8ba..b30de0ca5a 100644 --- a/Mage.Sets/src/mage/cards/s/SanguinePraetor.java +++ b/Mage.Sets/src/mage/cards/s/SanguinePraetor.java @@ -81,7 +81,7 @@ class SanguinePraetorEffect extends OneShotEffect { public SanguinePraetorEffect() { super(Outcome.Damage); - staticText = "Destroy each creature with the same converted mana cost as the sacrificed creature."; + staticText = "Destroy each creature with the same converted mana cost as the sacrificed creature"; } public SanguinePraetorEffect(final SanguinePraetorEffect effect) { @@ -98,7 +98,7 @@ class SanguinePraetorEffect extends OneShotEffect { } } - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), game)) { if (permanent.getConvertedManaCost() == cmc) { permanent.destroy(source.getSourceId(), game, false); } From b974a78b1bef2a1ea9276d86afe6c6de79bb49af Mon Sep 17 00:00:00 2001 From: Pete Rossi Date: Sun, 20 Nov 2016 22:33:08 -0800 Subject: [PATCH 15/18] Add magidex.com as additional image source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Images on magidex are referenced by url-escaped card name, so the implementation was straightforward. Flipped and Two-faced cards didn’t require any additional work to support. I downloaded all the images, and the only missing cards I could see were tokens. Seems to work pretty well. Fixes issue #2153 --- .../card/dl/sources/MagidexImageSource.java | 104 ++++++++++++++++++ .../plugins/card/images/DownloadPictures.java | 7 +- 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java new file mode 100644 index 0000000000..7a39d6768a --- /dev/null +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java @@ -0,0 +1,104 @@ +/* +* 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.plugins.card.dl.sources; + +import org.mage.plugins.card.images.CardDownloadData; +import java.net.URI; + +/** + * + * @author Pete Rossi + */ + +public class MagidexImageSource implements CardImageSource { + private static CardImageSource instance = new MagidexImageSource(); + + public static CardImageSource getInstance() { + if (instance == null) { + instance = new MagidexImageSource(); + } + return instance; + } + @Override + public String getSourceName() { + return "magidex.com"; + } + + @Override + public String getNextHttpImageUrl() { + return null; + } + + @Override + public String getFileForHttpImage(String httpImageUrl) { + return null; + } + + @Override + public String generateURL(CardDownloadData card) throws Exception { + String cardDownloadName = card.getDownloadName().toLowerCase(); + String cardSet = card.getSet(); + + if (cardDownloadName == null || cardSet == null) { + throw new Exception("Wrong parameters for image: cardDownloadName: " + cardDownloadName + ",card set: " + cardSet); + } + + if (card.isSplitCard()) { + cardDownloadName = cardDownloadName.replaceAll(" // ", ""); + } + + // This will properly escape the url + URI uri = new URI("http", "magidex.com", "/extstatic/card/" + cardSet + "/" + cardDownloadName + ".jpg", null, null); + return uri.toASCIIString(); + } + + @Override + public String generateTokenUrl(CardDownloadData card) { + return null; + } + + @Override + public Float getAverageSize() { + return 62.0f; + } + + @Override + public Integer getTotalImages() { + return -1; + } + + @Override + public Boolean isTokenSource() { + return false; + } + + @Override + public void doPause(String httpImageUrl) { + } +} diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java index 92b0a9f5a3..efb627fefc 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java @@ -64,6 +64,7 @@ import org.mage.plugins.card.dl.sources.MtgOnlTokensImageSource; import org.mage.plugins.card.dl.sources.MythicspoilerComSource; import org.mage.plugins.card.dl.sources.TokensMtgImageSource; import org.mage.plugins.card.dl.sources.WizardCardsImageSource; +import org.mage.plugins.card.dl.sources.MagidexImageSource; import org.mage.plugins.card.properties.SettingsManager; import org.mage.plugins.card.utils.CardImageUtils; @@ -149,7 +150,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab "tokens.mtg.onl", //"mtgimage.com (HQ)", "mtg.onl", "alternative.mtg.onl", - "GrabBag" + "GrabBag", + "magidex.com" //"mtgathering.ru HQ", //"mtgathering.ru MQ", //"mtgathering.ru LQ", @@ -186,6 +188,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab case 6: cardImageSource = GrabbagImageSource.getInstance(); break; + case 7: + cardImageSource = MagidexImageSource.getInstance(); + break; } updateCardsToDownload(); } From 9b80b1abb1168e55cc4cf9ea7f5e1fcd00bb0ce4 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 21 Nov 2016 19:34:20 +1100 Subject: [PATCH 16/18] Add 'EDH power level' rating to Commander tables --- .../src/mage/deck/Commander.java | 148 +++++++++++++----- 1 file changed, 113 insertions(+), 35 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 11fa3fbc50..c1c1037068 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -232,6 +232,7 @@ public class Commander extends Constructed { boolean gainControl = false; boolean infect = false; boolean mayCastForFree = false; + boolean miracle = false; boolean overload = false; boolean persist = false; boolean proliferate = false; @@ -262,6 +263,7 @@ public class Commander extends Constructed { gainControl |= s.contains("gain control"); infect |= s.contains("infect"); mayCastForFree |= s.contains("may cast") && s.contains("without paying"); + miracle |= s.contains("miracle"); overload |= s.contains("overload"); persist |= s.contains("persist"); proliferate |= s.contains("proliferate"); @@ -332,6 +334,9 @@ public class Commander extends Constructed { if (exile) { thisMaxPower = Math.max(thisMaxPower, 2); } + if (miracle) { + thisMaxPower = Math.max(thisMaxPower, 2); + } if (sliver) { thisMaxPower = Math.max(thisMaxPower, 2); } @@ -369,27 +374,79 @@ public class Commander extends Constructed { thisMaxPower = 0; } - // Banned in french + // Banned in french or unfair cards String cn = card.getName().toLowerCase(); - if (cn.equals("ancient tomb") || cn.equals("armageddon") - || cn.equals("aura shards") || cn.equals("back to basics") - || cn.equals("bane of progress") || cn.equals("basalt monolith") - || cn.equals("blightsteel collossus") || cn.equals("cabal coffers") - || cn.equals("craterhoof behemoth") || cn.equals("deepglow skate") - || cn.equals("dig through time") || cn.equals("entomb") - || cn.equals("food chain") || cn.equals("gaea's cradle") - || cn.equals("grim monolith") || cn.equals("hermit druid") - || cn.equals("humility") || cn.equals("imperial seal") - || cn.equals("karakas") || cn.equals("living death") - || cn.equals("loyal retainers") || cn.equals("mana crypt") - || cn.equals("mana drain") || cn.equals("mana vault") - || cn.equals("necrotic ooze") || cn.equals("oath of druids") - || cn.equals("protean hulk") || cn.equals("ravages of war") - || cn.equals("reclamation sage") || cn.equals("sensei's divning top") - || cn.equals("sol ring") || cn.equals("spore frog") - || cn.equals("strip mine") || cn.equals("the tabernacle at pendrell vale") - || cn.equals("tinker") || cn.equals("tolarian academy") - || cn.equals("winter orb") || cn.equals("treasure cruise")) { + if (cn.equals("ancient tomb") + || cn.equals("anafenza, the foremost") + || cn.equals("arcum dagsson") + || cn.equals("armageddon") + || cn.equals("aura shards") + || cn.equals("azami, lady of scrolls") + || cn.equals("back to basics") + || cn.equals("bane of progress") + || cn.equals("basalt monolith") + || cn.equals("blightsteel collossus") + || cn.equals("braids, cabal minion") + || cn.equals("cabal coffers") + || cn.equals("captain sisay") + || cn.equals("celestial dawn") + || cn.equals("child of alara") + || cn.equals("coalition relic") + || cn.equals("craterhoof behemoth") + || cn.equals("deepglow skate") + || cn.equals("derevi, empyrial tactician") + || cn.equals("dig through time") + || cn.equals("edric, spymaster of trest") + || cn.equals("elesh norn, grand cenobite") + || cn.equals("entomb") + || cn.equals("food chain") + || cn.equals("gaddock teeg") + || cn.equals("gaea's cradle") + || cn.equals("grand arbiter augustin iv") + || cn.equals("grim monolith") + || cn.equals("hermit druid") + || cn.equals("hokori, dust drinker") + || cn.equals("humility") + || cn.equals("imperial seal") + || cn.equals("iona, shield of emeria") + || cn.equals("jin-gitaxias, core augur") + || cn.equals("karador, ghost chieftain") + || cn.equals("karakas") + || cn.equals("kataki, war's wage") + || cn.equals("knowledge pool") + || cn.equals("linvala, keeper of silence") + || cn.equals("living death") + || cn.equals("llawan, cephalid empress") + || cn.equals("loyal retainers") + || cn.equals("malfegor") + || cn.equals("mana crypt") + || cn.equals("mana drain") + || cn.equals("mana vault") + || cn.equals("michiko konda, truth seeker") + || cn.equals("nath of the gilt-leaf") + || cn.equals("necrotic ooze") + || cn.equals("nicol bolas") + || cn.equals("numot, the devastator") + || cn.equals("oath of druids") + || cn.equals("protean hulk") + || cn.equals("purphoros, god of the forge") + || cn.equals("ravages of war") + || cn.equals("reclamation sage") + || cn.equals("sen triplets") + || cn.equals("serra's sanctum") + || cn.equals("sheoldred, whispering one") + || cn.equals("sol ring") + || cn.equals("spore frog") + || cn.equals("stasis") + || cn.equals("strip mine") + || cn.equals("the tabernacle at pendrell vale") + || cn.equals("tinker") + || cn.equals("tolarian academy") + || cn.equals("treasure cruise") + || cn.equals("urabrask the hidden") + || cn.equals("vorinclex, voice of hunger") + || cn.equals("winter orb") + || cn.equals("zur the enchanter")) { thisMaxPower = Math.max(thisMaxPower, 4); } @@ -437,34 +494,55 @@ public class Commander extends Constructed { || cn.equals("zealous conscripts")) { thisMaxPower = Math.max(thisMaxPower, 6); } - System.out.println(thisMaxPower + "Card:" + cn + " " + thisMaxPower); - edhPowerLevel += thisMaxPower; } for (Card commander : deck.getSideboard()) { int thisMaxPower = 0; String cn = commander.getName().toLowerCase(); + // Least fun commanders - if (cn.equals("memnarch") - || cn.equals("derevi, empyrial tactician") - || cn.equals("narset, enlightened master") - || cn.equals("nekusar, the mindrazer") - || cn.equals("norin the wary")) { + if (cn.equals("azami, lady of scrolls") + || cn.equals("braids, cabal minion") + || cn.equals("child of alara") + || cn.equals("derevi, empyrial tactician") + || cn.equals("edric, spymaster of trest") + || cn.equals("gaddock teeg") + || cn.equals("grand arbiter augustin iv") + || cn.equals("hokori, dust drinker") + || cn.equals("iona, shield of emeria") + || cn.equals("jin-gitaxias, core augur") + || cn.equals("karador, ghost chieftain") + || cn.equals("linvala, keeper of silence") + || cn.equals("llawan, cephalid empress") + || cn.equals("memnarch") + || cn.equals("meren of clan nel toth") + || cn.equals("michiko konda, truth seeker") + || cn.equals("narset, enlightened master") + || cn.equals("nekusar, the mindrazer") + || cn.equals("norin the wary") + || cn.equals("numot, the devastator") + || cn.equals("sheoldred, whispering one") + || cn.equals("teferi, mage of zhalfir") + || cn.equals("zur the enchanter")) { thisMaxPower = Math.max(thisMaxPower, 15); } // Next least fun commanders - if (cn.equals("meren of clan nel toth") - || cn.equals("teferi, mage of zhalfir") - || cn.equals("azami, lady of scrolls") - || cn.equals("brago, king eternal") - || cn.equals("mikaeus the unhallowed") - || cn.equals("memnarch")) { + if (cn.equals("anafenza, the foremost") + || cn.equals("arcum dagsson") + || cn.equals("brago, king eternal") + || cn.equals("captain sisay") + || cn.equals("elesh norn, grand cenobite") + || cn.equals("malfegor") + || cn.equals("mikaeus the unhallowed") + || cn.equals("nath of the gilt-leaf") + || cn.equals("purphoros, god of the forge") + || cn.equals("sen triplets") + || cn.equals("urabrask the hidden") + || cn.equals("vorinclex, voice of hunger")) { thisMaxPower = Math.max(thisMaxPower, 10); } - - System.out.println(thisMaxPower + "Card:" + cn + " bad commander" + thisMaxPower); edhPowerLevel += thisMaxPower; } From 713854bb434804e8af01d4b636ba07241966516f Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 21 Nov 2016 19:40:08 +1100 Subject: [PATCH 17/18] Add 'EDH power level' rating to Commander tables --- .../Mage.Deck.Constructed/src/mage/deck/Commander.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index c1c1037068..db6f671d22 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -382,6 +382,7 @@ public class Commander extends Constructed { || cn.equals("armageddon") || cn.equals("aura shards") || cn.equals("azami, lady of scrolls") + || cn.equals("azusa, lost but seeking") || cn.equals("back to basics") || cn.equals("bane of progress") || cn.equals("basalt monolith") @@ -418,16 +419,19 @@ public class Commander extends Constructed { || cn.equals("living death") || cn.equals("llawan, cephalid empress") || cn.equals("loyal retainers") + || cn.equals("maelstrom wanderer") || cn.equals("malfegor") || cn.equals("mana crypt") || cn.equals("mana drain") || cn.equals("mana vault") || cn.equals("michiko konda, truth seeker") || cn.equals("nath of the gilt-leaf") + || cn.equals("natural order") || cn.equals("necrotic ooze") || cn.equals("nicol bolas") || cn.equals("numot, the devastator") || cn.equals("oath of druids") + || cn.equals("pattern of rebirth") || cn.equals("protean hulk") || cn.equals("purphoros, god of the forge") || cn.equals("ravages of war") @@ -531,10 +535,12 @@ public class Commander extends Constructed { // Next least fun commanders if (cn.equals("anafenza, the foremost") || cn.equals("arcum dagsson") + || cn.equals("azusa, lost but seeking") || cn.equals("brago, king eternal") || cn.equals("captain sisay") || cn.equals("elesh norn, grand cenobite") || cn.equals("malfegor") + || cn.equals("maelstrom wanderer") || cn.equals("mikaeus the unhallowed") || cn.equals("nath of the gilt-leaf") || cn.equals("purphoros, god of the forge") From 9ff3e2c670c4f1c60bea72878ea60ea73f696efe Mon Sep 17 00:00:00 2001 From: Dilnu Date: Mon, 21 Nov 2016 07:39:47 -0500 Subject: [PATCH 18/18] Fix the counter removal code so it doesn't throw events when it's removing nonexistent counters. --- Mage/src/main/java/mage/cards/CardImpl.java | 4 +++- Mage/src/main/java/mage/counters/Counters.java | 14 +++++++++----- Mage/src/main/java/mage/players/PlayerImpl.java | 4 +++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index a67267fe39..d5965330f5 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -656,7 +656,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public void removeCounters(String name, int amount, Game game) { for (int i = 0; i < amount; i++) { - getCounters(game).removeCounter(name, 1); + if (!getCounters(game).removeCounter(name, 1)) { + break; + } GameEvent event = GameEvent.getEvent(GameEvent.EventType.COUNTER_REMOVED, objectId, getControllerOrOwner()); event.setData(name); game.fireEvent(event); diff --git a/Mage/src/main/java/mage/counters/Counters.java b/Mage/src/main/java/mage/counters/Counters.java index 21842e5978..c52c7d6cef 100644 --- a/Mage/src/main/java/mage/counters/Counters.java +++ b/Mage/src/main/java/mage/counters/Counters.java @@ -74,26 +74,30 @@ public class Counters extends HashMap implements Serializable { } } - public void removeCounter(String name) { - removeCounter(name, 1); + public boolean removeCounter(String name) { + return removeCounter(name, 1); } - public void removeCounter(CounterType counterType, int amount) { + public boolean removeCounter(CounterType counterType, int amount) { if (this.containsKey(counterType.getName())) { get(counterType.getName()).remove(amount); if (get(counterType.getName()).count == 0) { this.remove(counterType.getName()); - } + } + return true; } + return false; } - public void removeCounter(String name, int amount) { + public boolean removeCounter(String name, int amount) { if (this.containsKey(name)) { this.get(name).remove(amount); if (this.get(name).getCount() == 0) { this.remove(name); } + return true; } + return false; } public void removeAllCounters(CounterType counterType){ diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 039f0b7075..15a6f39a60 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1877,7 +1877,9 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void removeCounters(String name, int amount, Ability source, Game game) { for (int i = 0; i < amount; i++) { - counters.removeCounter(name, 1); + if (!counters.removeCounter(name, 1)) { + break; + } GameEvent event = GameEvent.getEvent(GameEvent.EventType.COUNTER_REMOVED, getId(), (source == null ? null : source.getSourceId()), (source == null ? null : source.getControllerId())); event.setData(name);