Skip to content

Commit db2a3b7

Browse files
committed
[WAR] added Spark Double
1 parent 4c899a2 commit db2a3b7

File tree

4 files changed

+264
-1
lines changed

4 files changed

+264
-1
lines changed
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package mage.cards.s;
2+
3+
import mage.MageInt;
4+
import mage.MageObject;
5+
import mage.abilities.Ability;
6+
import mage.abilities.common.EntersBattlefieldAbility;
7+
import mage.abilities.effects.Effect;
8+
import mage.abilities.effects.common.CopyPermanentEffect;
9+
import mage.cards.CardImpl;
10+
import mage.cards.CardSetInfo;
11+
import mage.constants.CardType;
12+
import mage.constants.SubType;
13+
import mage.constants.SuperType;
14+
import mage.counters.CounterType;
15+
import mage.filter.FilterPermanent;
16+
import mage.filter.common.FilterControlledPermanent;
17+
import mage.filter.predicate.Predicates;
18+
import mage.filter.predicate.mageobject.CardTypePredicate;
19+
import mage.game.Game;
20+
import mage.game.permanent.Permanent;
21+
import mage.util.functions.ApplyToPermanent;
22+
23+
import java.util.UUID;
24+
25+
/**
26+
* @author JayDi85
27+
*/
28+
public final class SparkDouble extends CardImpl {
29+
30+
private static FilterPermanent filter = new FilterControlledPermanent("a creature or planeswalker you control");
31+
32+
static {
33+
filter.add(Predicates.or(
34+
new CardTypePredicate(CardType.CREATURE),
35+
new CardTypePredicate(CardType.PLANESWALKER)));
36+
}
37+
38+
public SparkDouble(UUID ownerId, CardSetInfo setInfo) {
39+
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
40+
this.subtype.add(SubType.ILLUSION);
41+
this.power = new MageInt(0);
42+
this.toughness = new MageInt(0);
43+
44+
// You may have Spark Double enter the battlefield as a copy of a creature or planeswalker you control,
45+
// except it enters with an additional +1/+1 counter on it if it’s a creature,
46+
// it enters with an additional loyalty counter on it if it’s a planeswalker, and it isn’t legendary if that permanent is legendary.
47+
Effect effect = new CopyPermanentEffect(filter, new SparkDoubleExceptEffectsApplyerToPermanent());
48+
effect.setText("as a copy of a creature or planeswalker you control, "
49+
+ "except it enters with an additional +1/+1 counter on it if it’s a creature, "
50+
+ "it enters with an additional loyalty counter on it if it’s a planeswalker, and it isn’t legendary if that permanent is legendary.");
51+
EntersBattlefieldAbility ability = new EntersBattlefieldAbility(effect, true);
52+
this.addAbility(ability);
53+
}
54+
55+
public SparkDouble(final SparkDouble card) {
56+
super(card);
57+
}
58+
59+
@Override
60+
public SparkDouble copy() {
61+
return new SparkDouble(this);
62+
}
63+
}
64+
65+
class SparkDoubleExceptEffectsApplyerToPermanent extends ApplyToPermanent {
66+
67+
@Override
68+
public boolean apply(Game game, Permanent copyFromBlueprint, Ability source, UUID copyToObjectId) {
69+
return apply(game, (MageObject) copyFromBlueprint, source, copyToObjectId);
70+
}
71+
72+
@Override
73+
public boolean apply(Game game, MageObject copyFromBlueprint, Ability source, UUID copyToObjectId) {
74+
Permanent destCard = game.getPermanentEntering(copyToObjectId);
75+
if (destCard == null) {
76+
return false;
77+
}
78+
79+
// it isn’t legendary if that permanent is legendary
80+
copyFromBlueprint.getSuperType().remove(SuperType.LEGENDARY);
81+
82+
// TODO: Blood Moon problem, can't apply on type changing effects (same as TeferisTimeTwist)
83+
// see https://magic.wizards.com/en/articles/archive/feature/war-spark-release-notes-2019-04-19
84+
// If the copied permanent is affected by a type-changing effect, Spark Double may enter the battlefield with
85+
// different permanent types than the copied permanent currently has. Use the characteristics of Spark Double as
86+
// it enters the battlefield, not of the copied permanent, to determine whether it enters with an additional
87+
// counter on it. Notably, if Spark Double copies a Gideon planeswalker that's a creature because its loyalty
88+
// ability caused it to become a planeswalker creature, Spark Double enters as a noncreature planeswalker and
89+
// doesn't get a +1/+1 counter. On the other hand, if Spark Double copies Gideon Blackblade during your turn,
90+
// Spark Double enters as a planeswalker creature and gets both kinds of counters.
91+
92+
// enters with an additional +1/+1 counter on it if it’s a creature
93+
if (copyFromBlueprint.isCreature()) {
94+
destCard.addCounters(CounterType.P1P1.createInstance(), source, game);
95+
}
96+
97+
// enters with an additional loyalty counter on it if it’s a planeswalker
98+
if (copyFromBlueprint.isPlaneswalker()) {
99+
destCard.addCounters(CounterType.LOYALTY.createInstance(), source, game);
100+
}
101+
102+
return true;
103+
}
104+
105+
}

Mage.Sets/src/mage/cards/t/TeferisTimeTwist.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public boolean apply(Game game, Ability source) {
113113
}
114114
Permanent permanent = game.getPermanent(card.getId());
115115
if (permanent != null && permanent.isCreature()) {
116-
// This is technically wrong as it should enter with the counters,
116+
// TODO: This is technically wrong as it should enter with the counters,
117117
// however there's currently no way to know that for sure
118118
// this is similar to the blood moon issue
119119
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);

Mage.Sets/src/mage/sets/WarOfTheSpark.java

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ private WarOfTheSpark() {
245245
cards.add(new SetCardInfo("Sorin's Thirst", 104, Rarity.COMMON, mage.cards.s.SorinsThirst.class));
246246
cards.add(new SetCardInfo("Sorin, Vengeful Bloodlord", 217, Rarity.RARE, mage.cards.s.SorinVengefulBloodlord.class));
247247
cards.add(new SetCardInfo("Soul Diviner", 218, Rarity.RARE, mage.cards.s.SoulDiviner.class));
248+
cards.add(new SetCardInfo("Spark Double", 68, Rarity.RARE, mage.cards.s.SparkDouble.class));
248249
cards.add(new SetCardInfo("Spark Harvest", 105, Rarity.COMMON, mage.cards.s.SparkHarvest.class));
249250
cards.add(new SetCardInfo("Spark Reaper", 106, Rarity.COMMON, mage.cards.s.SparkReaper.class));
250251
cards.add(new SetCardInfo("Spellgorger Weird", 145, Rarity.COMMON, mage.cards.s.SpellgorgerWeird.class));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package org.mage.test.cards.copy;
2+
3+
import mage.abilities.keyword.VigilanceAbility;
4+
import mage.constants.CardType;
5+
import mage.constants.PhaseStep;
6+
import mage.constants.Zone;
7+
import mage.counters.CounterType;
8+
import mage.game.Game;
9+
import mage.game.permanent.Permanent;
10+
import org.junit.Assert;
11+
import org.junit.Ignore;
12+
import org.junit.Test;
13+
import org.mage.test.serverside.base.CardTestPlayerBase;
14+
15+
/**
16+
* @author JayDi85
17+
*/
18+
public class SparkDoubleTest extends CardTestPlayerBase {
19+
20+
private Permanent findDoubleSparkPermanent(Game game) {
21+
for (Permanent perm : game.getBattlefield().getAllActivePermanents()) {
22+
if (perm.isCopy()) {
23+
return perm;
24+
}
25+
}
26+
Assert.fail("spark must exist");
27+
return null;
28+
}
29+
30+
@Test
31+
public void test_CopyCreatureAndGetOneCounter() {
32+
addCard(Zone.BATTLEFIELD, playerA, "Abbey Griffin", 1); // 2/2, fly, vig
33+
//
34+
addCard(Zone.HAND, playerA, "Spark Double"); // {3}{U}
35+
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
36+
37+
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double");
38+
setChoice(playerA, "Yes");
39+
setChoice(playerA, "Abbey Griffin");
40+
41+
setStrictChooseMode(true);
42+
setStopAt(1, PhaseStep.BEGIN_COMBAT);
43+
execute();
44+
assertAllCommandsUsed();
45+
46+
assertPermanentCount(playerA, "Abbey Griffin", 2);
47+
48+
Permanent spark = findDoubleSparkPermanent(currentGame);
49+
Assert.assertEquals("must add 1 counter", 1, spark.getCounters(currentGame).getCount(CounterType.P1P1));
50+
//
51+
Assert.assertEquals("must copy p/t", 3, spark.getPower().getValue());
52+
Assert.assertEquals("must copy p/t", 3, spark.getToughness().getValue());
53+
Assert.assertTrue("must copy ability", spark.getAbilities().contains(VigilanceAbility.getInstance()));
54+
}
55+
56+
@Test
57+
public void test_CopyPlaneswalkerWithoutLegendaryWithOneCounter() {
58+
addCard(Zone.BATTLEFIELD, playerA, "Ajani, the Greathearted", 1);
59+
//
60+
addCard(Zone.HAND, playerA, "Spark Double"); // {3}{U}
61+
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
62+
63+
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double");
64+
setChoice(playerA, "Yes");
65+
setChoice(playerA, "Ajani, the Greathearted");
66+
67+
setStrictChooseMode(true);
68+
setStopAt(1, PhaseStep.BEGIN_COMBAT);
69+
execute();
70+
assertAllCommandsUsed();
71+
72+
assertPermanentCount(playerA, "Ajani, the Greathearted", 2);
73+
74+
Permanent spark = findDoubleSparkPermanent(currentGame);
75+
Assert.assertEquals("must add 1 loyalty", 5 + 1, spark.getCounters(currentGame).getCount(CounterType.LOYALTY));
76+
}
77+
78+
@Test
79+
public void test_CopyCreatureAndGetDoubleCounter() {
80+
addCard(Zone.BATTLEFIELD, playerA, "Abbey Griffin", 1); // 2/2, fly, vig
81+
addCard(Zone.BATTLEFIELD, playerA, "Doubling Season", 1);
82+
//
83+
addCard(Zone.HAND, playerA, "Spark Double"); // {3}{U}
84+
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
85+
86+
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double");
87+
setChoice(playerA, "Yes");
88+
setChoice(playerA, "Abbey Griffin");
89+
90+
setStrictChooseMode(true);
91+
setStopAt(1, PhaseStep.BEGIN_COMBAT);
92+
execute();
93+
assertAllCommandsUsed();
94+
95+
assertPermanentCount(playerA, "Abbey Griffin", 2);
96+
97+
Permanent spark = findDoubleSparkPermanent(currentGame);
98+
Assert.assertEquals("must add 2 counter", 2, spark.getCounters(currentGame).getCount(CounterType.P1P1));
99+
}
100+
101+
@Test
102+
public void test_CopyPlaneswalkerWithCreatureActivated() {
103+
addCard(Zone.BATTLEFIELD, playerA, "Gideon, Ally of Zendikar", 1);
104+
//
105+
addCard(Zone.HAND, playerA, "Spark Double"); // {3}{U}
106+
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
107+
108+
// activate creature ability
109+
checkType("planeswalker not creature", 1, PhaseStep.UPKEEP, playerA, "Gideon, Ally of Zendikar", CardType.CREATURE, false);
110+
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:");
111+
checkType("planeswalker is creature", 1, PhaseStep.BEGIN_COMBAT, playerA, "Gideon, Ally of Zendikar", CardType.CREATURE, true);
112+
113+
// copy
114+
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Spark Double");
115+
setChoice(playerA, "Yes");
116+
setChoice(playerA, "Gideon, Ally of Zendikar");
117+
118+
setStrictChooseMode(true);
119+
setStopAt(1, PhaseStep.END_TURN);
120+
execute();
121+
assertAllCommandsUsed();
122+
123+
assertPermanentCount(playerA, "Gideon, Ally of Zendikar", 2);
124+
125+
Permanent spark = findDoubleSparkPermanent(currentGame);
126+
Assert.assertEquals("must add 1 loyalty", 4 + 1, spark.getCounters(currentGame).getCount(CounterType.LOYALTY));
127+
Assert.assertEquals("must not add creature counter", 0, spark.getCounters(currentGame).getCount(CounterType.P1P1));
128+
}
129+
130+
@Test
131+
@Ignore // TODO: enabled after Blood Moon type changing effect will be fixed
132+
public void test_CopyPlaneswalkerWithCreatureTypeChangedEffect() {
133+
addCard(Zone.BATTLEFIELD, playerA, "Gideon Blackblade", 1);
134+
//
135+
addCard(Zone.HAND, playerA, "Spark Double"); // {3}{U}
136+
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
137+
138+
// Gideon Blackblade is creature on your turn (by type changing effect)
139+
checkType("planeswalker is creature", 1, PhaseStep.UPKEEP, playerA, "Gideon Blackblade", CardType.CREATURE, true);
140+
141+
// copy
142+
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double");
143+
setChoice(playerA, "Yes");
144+
setChoice(playerA, "Gideon Blackblade");
145+
146+
setStrictChooseMode(true);
147+
setStopAt(1, PhaseStep.END_COMBAT);
148+
execute();
149+
assertAllCommandsUsed();
150+
151+
assertPermanentCount(playerA, "Gideon Blackblade", 2);
152+
153+
Permanent spark = findDoubleSparkPermanent(currentGame);
154+
Assert.assertEquals("must add 1 loyalty", 4 + 1, spark.getCounters(currentGame).getCount(CounterType.LOYALTY));
155+
Assert.assertEquals("must add 1 creature counter", 1, spark.getCounters(currentGame).getCount(CounterType.P1P1));
156+
}
157+
}

0 commit comments

Comments
 (0)