From c109fc8a0e2d2d22cc89b3664ba327d5ffce1ca5 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 19 Mar 2017 12:23:58 -0400 Subject: [PATCH 1/3] Implemented card Lightning Coils and test for it --- .../src/mage/cards/l/LightningCoils.java | 135 ++++++++++++++++++ Mage.Sets/src/mage/sets/Mirrodin.java | 1 + .../cards/single/mir/LightningCoilsTest.java | 49 +++++++ .../game/permanent/token/ElementalToken.java | 16 ++- 4 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/l/LightningCoils.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java new file mode 100644 index 00000000000..4de088594a7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java @@ -0,0 +1,135 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.l; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ElementalToken; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author escplan9 - Derek Monturo + */ +public class LightningCoils extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nontoken creature you control"); + static { + filter.add(new ControllerPredicate(TargetController.YOU)); + filter.add(Predicates.not(new TokenPredicate())); + } + + public LightningCoils(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils. + this.addAbility( + new DiesCreatureTriggeredAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), + false, filter)); + + // At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it + // and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. + // Exile them at the beginning of the next end step. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new LightningCoilsEffect(), TargetController.YOU, false)); + } + + public LightningCoils(final LightningCoils card) { + super(card); + } + + @Override + public LightningCoils copy() { + return new LightningCoils(this); + } +} + +class LightningCoilsEffect extends OneShotEffect { + + LightningCoilsEffect() { + super(Outcome.Benefit); + staticText = "if {this} has five or more charge counters on it, remove all of them from it and put that man 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step."; + } + + LightningCoilsEffect(final LightningCoilsEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent p = game.getPermanent(source.getSourceId()); + if (p != null && controller != null) { + int counters = p.getCounters(game).getCount(CounterType.CHARGE); + if (counters >= 5) { + // remove all the counters and create that many tokens + p.removeCounters(CounterType.CHARGE.getName(), p.getCounters(game).getCount(CounterType.CHARGE), game); + CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), counters); + effect.apply(game, source); + + // exile those tokens at next end step + for (UUID tokenId : effect.getLastAddedTokenIds()) { + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent != null) { + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); + } + } + return true; + } + } + return false; + } + + @Override + public LightningCoilsEffect copy() { + return new LightningCoilsEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Mirrodin.java b/Mage.Sets/src/mage/sets/Mirrodin.java index 1cc14bfb61d..a5692d782f7 100644 --- a/Mage.Sets/src/mage/sets/Mirrodin.java +++ b/Mage.Sets/src/mage/sets/Mirrodin.java @@ -142,6 +142,7 @@ public class Mirrodin extends ExpansionSet { cards.add(new SetCardInfo("Leonin Sun Standard", 194, Rarity.RARE, mage.cards.l.LeoninSunStandard.class)); cards.add(new SetCardInfo("Leveler", 195, Rarity.RARE, mage.cards.l.Leveler.class)); cards.add(new SetCardInfo("Lifespark Spellbomb", 197, Rarity.COMMON, mage.cards.l.LifesparkSpellbomb.class)); + cards.add(new SetCardInfo("Lightning Coils", 198, Rarity.RARE, mage.cards.l.LightningCoils.class)); cards.add(new SetCardInfo("Lightning Greaves", 199, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class)); cards.add(new SetCardInfo("Living Hive", 124, Rarity.RARE, mage.cards.l.LivingHive.class)); cards.add(new SetCardInfo("Lodestone Myr", 200, Rarity.RARE, mage.cards.l.LodestoneMyr.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java new file mode 100644 index 00000000000..53289d21156 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.mir; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author escplan9 (Derek Monturo) + */ +public class LightningCoilsTest extends CardTestPlayerBase { + + @Test + public void sacrificeSixCreaturesProducesSixElementals() { + + /* + Lightning Coils {3} + Artifact + Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils. + At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it + and create that many 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step. + */ + String lCoils = "Lightning Coils"; + + /* + Bottle Gnomes {3} + Artifact Creature — Gnome + Sacrifice Bottle Gnomes: You gain 3 life. + */ + String bGnomes = "Bottle Gnomes"; + int gnomeCount = 6; + + addCard(Zone.BATTLEFIELD, playerA, lCoils); + addCard(Zone.BATTLEFIELD, playerA, bGnomes, gnomeCount); + + for (int i = 0; i < gnomeCount; i++) // sac Gnomes 6 times + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, bGnomes, gnomeCount); + assertPermanentCount(playerA, lCoils, 1); + assertCounterCount(playerA, lCoils, CounterType.CHARGE, 0); + assertPermanentCount(playerA, "Elemental", gnomeCount); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java index 5ba2207bf7a..141a07a221b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import mage.MageInt; +import mage.abilities.keyword.HasteAbility; import mage.constants.CardType; /** @@ -58,5 +59,18 @@ public class ElementalToken extends Token { this.setOriginalExpansionSetCode("CON"); } - + + public ElementalToken(String setCode, int tokenType, boolean hasHaste) { + super("Elemental", "3/1 red Elemental creature token"); + setTokenType(tokenType); + availableImageSetCodes = tokenImageSets; + setOriginalExpansionSetCode(setCode); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add("Elemental"); + power = new MageInt(3); + toughness = new MageInt(1); + + if (hasHaste) this.addAbility(HasteAbility.getInstance()); + } } \ No newline at end of file From dcd5c76bad0d96520a8dd55262c24e14cd908660 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 19 Mar 2017 12:33:12 -0400 Subject: [PATCH 2/3] minor typo fix --- Mage.Sets/src/mage/cards/l/LightningCoils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java index 4de088594a7..d6b52be2a41 100644 --- a/Mage.Sets/src/mage/cards/l/LightningCoils.java +++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java @@ -94,7 +94,7 @@ class LightningCoilsEffect extends OneShotEffect { LightningCoilsEffect() { super(Outcome.Benefit); - staticText = "if {this} has five or more charge counters on it, remove all of them from it and put that man 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step."; + staticText = "if {this} has five or more charge counters on it, remove all of them from it and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step."; } LightningCoilsEffect(final LightningCoilsEffect effect) { From 311e41abd26b7a4155a7f0b26c40505d3b60fda7 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 19 Mar 2017 17:54:07 -0400 Subject: [PATCH 3/3] refactored exiletokensAtEndStep --- .../src/mage/cards/l/LightningCoils.java | 13 +---- .../cards/single/mir/LightningCoilsTest.java | 50 +++++++++++++++++++ .../effects/common/CreateTokenEffect.java | 16 +++++- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java index d6b52be2a41..c0b56441fd3 100644 --- a/Mage.Sets/src/mage/cards/l/LightningCoils.java +++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java @@ -31,17 +31,14 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -51,7 +48,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ElementalToken; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -114,14 +110,7 @@ class LightningCoilsEffect extends OneShotEffect { effect.apply(game, source); // exile those tokens at next end step - for (UUID tokenId : effect.getLastAddedTokenIds()) { - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); - } - } + effect.exileTokensCreatedAtNextEndStep(game, source); return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java index 53289d21156..d98c8353f6b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/LightningCoilsTest.java @@ -46,4 +46,54 @@ public class LightningCoilsTest extends CardTestPlayerBase { assertCounterCount(playerA, lCoils, CounterType.CHARGE, 0); assertPermanentCount(playerA, "Elemental", gnomeCount); } + + @Test + public void sacrificeSixCreaturesProducesSixElementalsExiledAtEnd() { + + /* + Lightning Coils {3} + Artifact + Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils. + At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it + and create that many 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step. + */ + String lCoils = "Lightning Coils"; + + /* + Bottle Gnomes {3} + Artifact Creature — Gnome + Sacrifice Bottle Gnomes: You gain 3 life. + */ + String bGnomes = "Bottle Gnomes"; + + /* + Grand Melee {3}{R} + Enchantment + All creatures attack each turn if able. + All creatures block each turn if able. + */ + String gMelee = "Grand Melee"; + + int tokenCount = 5; + + addCard(Zone.BATTLEFIELD, playerA, lCoils); + addCard(Zone.BATTLEFIELD, playerA, bGnomes, tokenCount); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, gMelee); + + for (int i = 0; i < tokenCount; i++) // sac Gnomes 5 times + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, gMelee); + setStopAt(3, PhaseStep.CLEANUP); + execute(); + + assertPermanentCount(playerA, gMelee, 1); + assertGraveyardCount(playerA, bGnomes, tokenCount); + assertPermanentCount(playerA, lCoils, 1); + assertCounterCount(playerA, lCoils, CounterType.CHARGE, 0); + assertPermanentCount(playerA, "Elemental", 0); + int remainingLife = 20 - (tokenCount * 3); // each elemental does 3 damage + assertLife(playerB, remainingLife); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java index 72d68417154..964fa5f99df 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java @@ -30,12 +30,16 @@ package mage.abilities.effects.common; import java.util.ArrayList; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -108,6 +112,17 @@ public class CreateTokenEffect extends OneShotEffect { public ArrayList getLastAddedTokenIds() { return lastAddedTokenIds; } + + public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { + for (UUID tokenId : this.getLastAddedTokenIds()) { + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent != null) { + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); + } + } + } private void setText() { StringBuilder sb = new StringBuilder("create "); @@ -144,5 +159,4 @@ public class CreateTokenEffect extends OneShotEffect { sb.append(message); staticText = sb.toString(); } - }