mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Additional and alternative costs improved:
* Now player must choose additional costs before ability's modes;
* Fixed broken kicker ability from ZNR (see comments from d4ca287f0f
);
* Improved compatibility of additional cost with cost modification effects (fixed that optional multi-costs doesn't affected by cost modification);
* Improved compatibility of additional cost with alternative cost (some cards ignores additional cost on alternative usage, e.g. on play free);
This commit is contained in:
parent
586538a66c
commit
6e0c7e868c
17 changed files with 504 additions and 156 deletions
|
@ -8,7 +8,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.FightTargetsEffect;
|
import mage.abilities.effects.common.FightTargetsEffect;
|
||||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||||
import mage.abilities.keyword.KickerAbility;
|
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
@ -33,12 +33,11 @@ public final class InscriptionOfAbundance extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}");
|
||||||
|
|
||||||
// Kicker {2}{G}
|
// Kicker {2}{G}
|
||||||
this.addAbility(new KickerAbility(new ManaCostsImpl<>("{2}{G}")));
|
this.addAbility(new KickerWithAnyNumberModesAbility(new ManaCostsImpl<>("{2}{G}")));
|
||||||
|
|
||||||
// Choose one. If this spell was kicked, choose any number instead.
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
|
||||||
|
|
||||||
// • Put two +1/+1 counters on target creature.
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
this.getSpellAbility().getModes().setChooseText("choose one. If this spell was kicked, choose any number instead.");
|
||||||
this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)));
|
this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)));
|
||||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
import mage.abilities.effects.common.ReturnToHandTargetEffect;
|
import mage.abilities.effects.common.ReturnToHandTargetEffect;
|
||||||
import mage.abilities.effects.keyword.ScryEffect;
|
import mage.abilities.effects.keyword.ScryEffect;
|
||||||
import mage.abilities.keyword.KickerAbility;
|
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
@ -29,12 +29,11 @@ public final class InscriptionOfInsight extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
|
||||||
|
|
||||||
// Kicker {2}{U}{U}
|
// Kicker {2}{U}{U}
|
||||||
this.addAbility(new KickerAbility(new ManaCostsImpl<>("{2}{U}{U}")));
|
this.addAbility(new KickerWithAnyNumberModesAbility(new ManaCostsImpl<>("{2}{U}{U}")));
|
||||||
|
|
||||||
// Choose one. If this spell was kicked, choose any number instead.
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
|
||||||
|
|
||||||
// • Return up to two target creatures to their owners' hands.
|
// • Return up to two target creatures to their owners' hands.
|
||||||
|
this.getSpellAbility().getModes().setChooseText("choose one. If this spell was kicked, choose any number instead.");
|
||||||
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
|
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
|
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||||
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||||
import mage.abilities.effects.common.discard.DiscardTargetEffect;
|
import mage.abilities.effects.common.discard.DiscardTargetEffect;
|
||||||
import mage.abilities.keyword.KickerAbility;
|
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
@ -40,12 +40,11 @@ public final class InscriptionOfRuin extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
|
||||||
|
|
||||||
// Kicker {2}{B}{B}
|
// Kicker {2}{B}{B}
|
||||||
this.addAbility(new KickerAbility(new ManaCostsImpl<>("{2}{B}{B}")));
|
this.addAbility(new KickerWithAnyNumberModesAbility(new ManaCostsImpl<>("{2}{B}{B}")));
|
||||||
|
|
||||||
// Choose one. If this spell was kicked, choose any number instead.
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
|
||||||
|
|
||||||
// • Target opponent discards two cards.
|
// • Target opponent discards two cards.
|
||||||
|
this.getSpellAbility().getModes().setChooseText("choose one. If this spell was kicked, choose any number instead.");
|
||||||
this.getSpellAbility().addEffect(new DiscardTargetEffect(2));
|
this.getSpellAbility().addEffect(new DiscardTargetEffect(2));
|
||||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.cards.abilities.keywords;
|
package org.mage.test.cards.abilities.keywords;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
|
@ -7,39 +6,172 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @author LevelX2, JayDi85
|
||||||
* @author LevelX2
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class EntwineTest extends CardTestPlayerBase {
|
public class EntwineTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastWithoutEntwine() {
|
||||||
|
// Choose one —
|
||||||
|
//• Barbed Lightning deals 3 damage to target creature.
|
||||||
|
//• Barbed Lightning deals 3 damage to target player or planeswalker.
|
||||||
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
|
setChoice(playerA, "No"); // not use Entwine
|
||||||
|
setModeChoice(playerA, "1"); // target creature
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||||
|
assertTappedCount("Mountain", true, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastEntwine_Normal() {
|
||||||
|
// Choose one —
|
||||||
|
//• Barbed Lightning deals 3 damage to target creature.
|
||||||
|
//• Barbed Lightning deals 3 damage to target player or planeswalker.
|
||||||
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3 + 2);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
|
setChoice(playerA, "Yes"); // use Entwine
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
addTarget(playerA, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20 - 3);
|
||||||
|
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||||
|
assertTappedCount("Mountain", true, 3 + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastEntwine_CostReduction() {
|
||||||
|
addCustomEffect_SpellCostModification(playerA, -4);
|
||||||
|
|
||||||
|
// Choose one —
|
||||||
|
//• Barbed Lightning deals 3 damage to target creature.
|
||||||
|
//• Barbed Lightning deals 3 damage to target player or planeswalker.
|
||||||
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); // -4 as cost reduction
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
|
setChoice(playerA, "Yes"); // use Entwine
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
addTarget(playerA, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20 - 3);
|
||||||
|
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||||
|
assertTappedCount("Mountain", true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastEntwine_CostIncreasing() {
|
||||||
|
addCustomEffect_SpellCostModification(playerA, 5);
|
||||||
|
|
||||||
|
// Choose one —
|
||||||
|
//• Barbed Lightning deals 3 damage to target creature.
|
||||||
|
//• Barbed Lightning deals 3 damage to target player or planeswalker.
|
||||||
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3 + 2 + 5);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
|
setChoice(playerA, "Yes"); // use Entwine
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
addTarget(playerA, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20 - 3);
|
||||||
|
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||||
|
assertTappedCount("Mountain", true, 3 + 2 + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastEntwine_FreeFromHand() {
|
||||||
|
// You may cast nonland cards from your hand without paying their mana costs.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
|
||||||
|
|
||||||
|
// Choose one —
|
||||||
|
//• Barbed Lightning deals 3 damage to target creature.
|
||||||
|
//• Barbed Lightning deals 3 damage to target player or planeswalker.
|
||||||
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // only Entwine pay need
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
|
setChoice(playerA, "Yes"); // cast for free
|
||||||
|
setChoice(playerA, "Yes"); // use Entwine
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
addTarget(playerA, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20 - 3);
|
||||||
|
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||||
|
assertTappedCount("Plains", true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_ToothAndNail() {
|
public void test_ToothAndNail() {
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
|
|
||||||
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 1);
|
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 1);
|
||||||
addCard(Zone.LIBRARY, playerA, "Pillarfield Ox", 1);
|
addCard(Zone.LIBRARY, playerA, "Pillarfield Ox", 1);
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 9);
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 9);
|
||||||
// Choose one -
|
// Choose one -
|
||||||
// Search your library for up to two creature cards, reveal them, put them into your hand, then shuffle your library;
|
// Search your library for up to two creature cards, reveal them, put them into your hand, then shuffle your library;
|
||||||
// or put up to two creature cards from your hand onto the battlefield.
|
// or put up to two creature cards from your hand onto the battlefield.
|
||||||
// Entwine {2}
|
// Entwine {2}
|
||||||
addCard(Zone.HAND, playerA, "Tooth and Nail"); // Sorcery {5}{G}{G}
|
addCard(Zone.HAND, playerA, "Tooth and Nail"); // Sorcery {5}{G}{G}
|
||||||
|
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tooth and Nail");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tooth and Nail");
|
||||||
setChoice(playerA, "Yes"); // Message: Pay Entwine {2} ?
|
setChoice(playerA, "Yes"); // Message: Pay Entwine {2} ?
|
||||||
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||||
|
setChoice(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||||
setChoice(playerA, "Silvercoat Lion^Pillarfield Ox");
|
|
||||||
|
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||||
assertPermanentCount(playerA, "Pillarfield Ox", 1);
|
assertPermanentCount(playerA, "Pillarfield Ox", 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,6 +400,7 @@ public class KickerTest extends CardTestPlayerBase {
|
||||||
assertTappedCount("Swamp", true, 5);
|
assertTappedCount("Swamp", true, 5);
|
||||||
assertGraveyardCount(playerA, "Marsh Casualties", 1);
|
assertGraveyardCount(playerA, "Marsh Casualties", 1);
|
||||||
assertPowerToughness(playerB, "Centaur Courser", 1, 1);
|
assertPowerToughness(playerB, "Centaur Courser", 1, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package org.mage.test.cards.abilities.keywords;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class KickerWithAnyNumberModesAbilityTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_WithoutKicker() {
|
||||||
|
// Kicker {2}{G}
|
||||||
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
// • Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
|
// • Target creature you control fights target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Inscription of Abundance", 1); // {1}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inscription of Abundance");
|
||||||
|
setChoice(playerA, "No"); // no kicker
|
||||||
|
setModeChoice(playerA, "1");
|
||||||
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Balduvian Bears", 2 + 2, 2 + 2);
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertTappedCount("Forest", true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Kicker_Normal() {
|
||||||
|
// Kicker {2}{G}
|
||||||
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
// • Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
|
// • Target creature you control fights target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Inscription of Abundance", 1); // {1}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 3);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inscription of Abundance");
|
||||||
|
setChoice(playerA, "Yes"); // use kicker
|
||||||
|
setModeChoice(playerA, "2");
|
||||||
|
setModeChoice(playerA, "1");
|
||||||
|
addTarget(playerA, playerA); // gain x life
|
||||||
|
addTarget(playerA, "Balduvian Bears"); // get counters
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Balduvian Bears", 2 + 2, 2 + 2);
|
||||||
|
assertLife(playerA, 20 + 4);
|
||||||
|
assertTappedCount("Forest", true, 2 + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Kicker_CostReduction() {
|
||||||
|
addCustomEffect_SpellCostModification(playerA, -4);
|
||||||
|
|
||||||
|
// Kicker {2}{G}
|
||||||
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
// • Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
|
// • Target creature you control fights target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Inscription of Abundance", 1); // {1}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 3 - 3); // -3 by cost reduction
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inscription of Abundance");
|
||||||
|
setChoice(playerA, "Yes"); // use kicker
|
||||||
|
setModeChoice(playerA, "2");
|
||||||
|
setModeChoice(playerA, "1");
|
||||||
|
addTarget(playerA, playerA); // gain x life
|
||||||
|
addTarget(playerA, "Balduvian Bears"); // get counters
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Balduvian Bears", 2 + 2, 2 + 2);
|
||||||
|
assertLife(playerA, 20 + 4);
|
||||||
|
assertTappedCount("Forest", true, 2 + 3 - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Kicker_CostIncreasing() {
|
||||||
|
addCustomEffect_SpellCostModification(playerA, 5);
|
||||||
|
|
||||||
|
// Kicker {2}{G}
|
||||||
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
// • Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
|
// • Target creature you control fights target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Inscription of Abundance", 1); // {1}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 3 + 5); // +5 by cost increase
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inscription of Abundance");
|
||||||
|
setChoice(playerA, "Yes"); // use kicker
|
||||||
|
setModeChoice(playerA, "2");
|
||||||
|
setModeChoice(playerA, "1");
|
||||||
|
addTarget(playerA, playerA); // gain x life
|
||||||
|
addTarget(playerA, "Balduvian Bears"); // get counters
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Balduvian Bears", 2 + 2, 2 + 2);
|
||||||
|
assertLife(playerA, 20 + 4);
|
||||||
|
assertTappedCount("Forest", true, 2 + 3 + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Kicker_FreeFromHand() {
|
||||||
|
// You may cast nonland cards from your hand without paying their mana costs.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
|
||||||
|
|
||||||
|
// Kicker {2}{G}
|
||||||
|
// Choose one. If this spell was kicked, choose any number instead.
|
||||||
|
// • Put two +1/+1 counters on target creature.
|
||||||
|
// • Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
|
// • Target creature you control fights target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Inscription of Abundance", 1); // {1}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); // -2 by free cast
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inscription of Abundance");
|
||||||
|
setChoice(playerA, "Yes"); // use free cast
|
||||||
|
setChoice(playerA, "Yes"); // use kicker
|
||||||
|
setModeChoice(playerA, "2");
|
||||||
|
setModeChoice(playerA, "1");
|
||||||
|
addTarget(playerA, playerA); // gain x life
|
||||||
|
addTarget(playerA, "Balduvian Bears"); // get counters
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Balduvian Bears", 2 + 2, 2 + 2);
|
||||||
|
assertLife(playerA, 20 + 4);
|
||||||
|
assertTappedCount("Forest", true, 3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,7 +192,7 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
|
||||||
/**
|
/**
|
||||||
* Omniscience is not allowing me to cast spells for free. I'm playing a
|
* Omniscience is not allowing me to cast spells for free. I'm playing a
|
||||||
* Commander game against the Computer, if that helps.
|
* Commander game against the Computer, if that helps.
|
||||||
*
|
* <p>
|
||||||
* Edit: It's not letting me cast fused spells for free. Others seems to be
|
* Edit: It's not letting me cast fused spells for free. Others seems to be
|
||||||
* working.
|
* working.
|
||||||
*/
|
*/
|
||||||
|
@ -276,16 +276,23 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
// Choose one - Barbed Lightning deals 3 damage to target creature; or Barbed Lightning deals 3 damage to target player.
|
// Choose one - Barbed Lightning deals 3 damage to target creature; or Barbed Lightning deals 3 damage to target player.
|
||||||
// Entwine {2} (Choose both if you pay the entwine cost.)
|
// Entwine {2} (Choose both if you pay the entwine cost.)
|
||||||
addCard(Zone.HAND, playerA, "Barbed Lightning", 1);
|
addCard(Zone.HAND, playerA, "Barbed Lightning", 1); // {2}{R}
|
||||||
|
|
||||||
// Creature - 3/3 Swampwalk
|
// Creature - 3/3 Swampwalk
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Bog Wraith", 1);
|
addCard(Zone.BATTLEFIELD, playerB, "Bog Wraith", 1);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning", "Bog Wraith");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||||
addTarget(playerA, playerB);
|
setChoice(playerA, "Yes"); // cast without cost
|
||||||
|
setChoice(playerA, "Yes"); // pay Entwine
|
||||||
|
addTarget(playerA, "Bog Wraith"); // target form mode 1
|
||||||
|
addTarget(playerA, playerB); // target for mode 2
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
showBattlefield("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Barbed Lightning", 1);
|
assertGraveyardCount(playerA, "Barbed Lightning", 1);
|
||||||
assertGraveyardCount(playerB, "Bog Wraith", 1);
|
assertGraveyardCount(playerB, "Bog Wraith", 1);
|
||||||
|
@ -293,7 +300,7 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
|
||||||
assertLife(playerA, 20);
|
assertLife(playerA, 20);
|
||||||
assertLife(playerB, 17);
|
assertLife(playerB, 17);
|
||||||
|
|
||||||
assertTapped("Plains", true); // plains have to be tapped because {2} from Entwine have to be paid
|
assertTappedCount("Plains", true, 2); // plains have to be tapped because {2} from Entwine have to be paid
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.cards.modal;
|
package org.mage.test.cards.modal;
|
||||||
|
|
||||||
import mage.abilities.keyword.SwampwalkAbility;
|
import mage.abilities.keyword.SwampwalkAbility;
|
||||||
|
@ -8,7 +7,6 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class ChooseOneTest extends CardTestPlayerBase {
|
public class ChooseOneTest extends CardTestPlayerBase {
|
||||||
|
@ -48,8 +46,10 @@ public class ChooseOneTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion");
|
||||||
setModeChoice(playerA, "2");
|
setModeChoice(playerA, "2");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Funeral Charm", 1);
|
assertGraveyardCount(playerA, "Funeral Charm", 1);
|
||||||
assertPowerToughness(playerB, "Silvercoat Lion", 4, 1);
|
assertPowerToughness(playerB, "Silvercoat Lion", 4, 1);
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageIdentifier;
|
import mage.MageIdentifier;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.costs.*;
|
import mage.abilities.costs.*;
|
||||||
|
@ -36,6 +32,11 @@ import mage.util.ThreadLocalStringBuilder;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
@ -227,22 +228,10 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
game.applyEffects();
|
game.applyEffects();
|
||||||
|
|
||||||
/* 20130201 - 601.2b
|
|
||||||
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
|
||||||
*/
|
|
||||||
if (!getModes().choose(game, this)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MageObject sourceObject = getSourceObject(game);
|
MageObject sourceObject = getSourceObject(game);
|
||||||
if (getSourceObjectZoneChangeCounter() == 0) {
|
if (getSourceObjectZoneChangeCounter() == 0) {
|
||||||
setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId()));
|
setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId()));
|
||||||
}
|
}
|
||||||
if (controller.isTestMode()) {
|
|
||||||
if (!controller.addTargets(this, game)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 20130201 - 601.2b
|
/* 20130201 - 601.2b
|
||||||
* If the player wishes to splice any cards onto the spell (see rule 702.45), he
|
* If the player wishes to splice any cards onto the spell (see rule 702.45), he
|
||||||
|
@ -268,9 +257,6 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getModes().getAdditionalCost() != null) {
|
|
||||||
getModes().getAdditionalCost().addOptionalAdditionalModeCosts(this, game);
|
|
||||||
}
|
|
||||||
// 20130201 - 601.2b
|
// 20130201 - 601.2b
|
||||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||||
|
@ -311,8 +297,29 @@ public abstract class AbilityImpl implements Ability {
|
||||||
|
|
||||||
handlePhyrexianManaCosts(game, sourceId, controller);
|
handlePhyrexianManaCosts(game, sourceId, controller);
|
||||||
|
|
||||||
|
/* 20130201 - 601.2b
|
||||||
|
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
||||||
|
*/
|
||||||
|
// rules:
|
||||||
|
// You kick a spell as you cast it. You declare whether you're going to pay a kicker cost at the same
|
||||||
|
// time you'd choose a spell's mode, and then you actually pay it at the same time you pay the spell's mana cost.
|
||||||
|
// Kicking a spell is always optional.
|
||||||
|
if (!getModes().choose(game, this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unit tests only: it allows to add targets/choices by two ways:
|
||||||
|
// 1. From cast/activate command params (process it here)
|
||||||
|
// 2. From single addTarget/setChoice, it's a preffered method for tests (process it in normal choose dialogs like human player)
|
||||||
|
if (controller.isTestMode()) {
|
||||||
|
if (!controller.addTargets(this, game)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (UUID modeId : this.getModes().getSelectedModes()) {
|
for (UUID modeId : this.getModes().getSelectedModes()) {
|
||||||
this.getModes().setActiveMode(modeId);
|
this.getModes().setActiveMode(modeId);
|
||||||
|
|
||||||
//20121001 - 601.2c
|
//20121001 - 601.2c
|
||||||
// 601.2c The player announces their choice of an appropriate player, object, or zone for
|
// 601.2c The player announces their choice of an appropriate player, object, or zone for
|
||||||
// each target the spell requires. A spell may require some targets only if an alternative or
|
// each target the spell requires. A spell may require some targets only if an alternative or
|
||||||
|
@ -333,8 +340,10 @@ public abstract class AbilityImpl implements Ability {
|
||||||
if (sourceObject != null && this.getAbilityType() != AbilityType.TRIGGERED) { // triggered abilities check this already in playerImpl.triggerAbility
|
if (sourceObject != null && this.getAbilityType() != AbilityType.TRIGGERED) { // triggered abilities check this already in playerImpl.triggerAbility
|
||||||
sourceObject.adjustTargets(this, game);
|
sourceObject.adjustTargets(this, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getTargets().isEmpty()) {
|
if (!getTargets().isEmpty()) {
|
||||||
Outcome outcome = getEffects().getOutcome(this);
|
Outcome outcome = getEffects().getOutcome(this);
|
||||||
|
|
||||||
// only activated abilities can be canceled by human user (not triggered)
|
// only activated abilities can be canceled by human user (not triggered)
|
||||||
boolean canCancel = this instanceof ActivatedAbility && controller.isHuman();
|
boolean canCancel = this instanceof ActivatedAbility && controller.isHuman();
|
||||||
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, canCancel)) {
|
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, canCancel)) {
|
||||||
|
@ -434,7 +443,11 @@ public abstract class AbilityImpl implements Ability {
|
||||||
|
|
||||||
boolean alternativeCostUsed = false;
|
boolean alternativeCostUsed = false;
|
||||||
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
||||||
|
// it's important to apply alternative cost first
|
||||||
|
// example: Omniscience gives free mana as alternative, but Entwine ability adds {2} as additional
|
||||||
Abilities<Ability> abilities = CardUtil.getAbilities(sourceObject, game);
|
Abilities<Ability> abilities = CardUtil.getAbilities(sourceObject, game);
|
||||||
|
|
||||||
|
// 1. ALTERNATIVE COSTS
|
||||||
for (Ability ability : abilities) {
|
for (Ability ability : abilities) {
|
||||||
// if cast for noMana no Alternative costs are allowed
|
// if cast for noMana no Alternative costs are allowed
|
||||||
if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
|
if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
|
||||||
|
@ -447,11 +460,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
|
|
||||||
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// controller specific alternate spell costs
|
// controller specific alternate spell costs
|
||||||
if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
|
if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
|
||||||
if (this.getAbilityType() == AbilityType.SPELL
|
if (this.getAbilityType() == AbilityType.SPELL
|
||||||
|
@ -469,6 +478,13 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. ADDITIONAL COST
|
||||||
|
for (Ability ability : abilities) {
|
||||||
|
if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
|
||||||
|
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return alternativeCostUsed;
|
return alternativeCostUsed;
|
||||||
|
@ -806,7 +822,18 @@ public abstract class AbilityImpl implements Ability {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCost(Cost cost) {
|
public void addCost(Cost cost) {
|
||||||
if (cost != null) {
|
if (cost == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cost instanceof Costs) {
|
||||||
|
// as list of costs
|
||||||
|
Costs<Cost> list = (Costs<Cost>) cost;
|
||||||
|
for (Cost single : list) {
|
||||||
|
addCost(single);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// as single cost
|
||||||
if (cost instanceof ManaCost) {
|
if (cost instanceof ManaCost) {
|
||||||
this.addManaCost((ManaCost) cost);
|
this.addManaCost((ManaCost) cost);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import mage.abilities.condition.common.KickedCondition;
|
|
||||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
@ -33,11 +32,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
private TargetController modeChooser;
|
private TargetController modeChooser;
|
||||||
private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice
|
private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice
|
||||||
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
|
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
|
||||||
private OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid
|
|
||||||
private Filter maxModesFilter = null; // calculates the max number of available modes
|
private Filter maxModesFilter = null; // calculates the max number of available modes
|
||||||
private boolean isRandom = false;
|
private boolean isRandom = false;
|
||||||
private String chooseText = null;
|
private String chooseText = null;
|
||||||
private boolean allWhenKicked = false;
|
|
||||||
private boolean resetEachTurn = false;
|
private boolean resetEachTurn = false;
|
||||||
private int turnNum = 0;
|
private int turnNum = 0;
|
||||||
|
|
||||||
|
@ -68,12 +65,10 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
this.modeChooser = modes.modeChooser;
|
this.modeChooser = modes.modeChooser;
|
||||||
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
||||||
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
||||||
this.optionalAdditionalModeSourceCosts = modes.optionalAdditionalModeSourceCosts;
|
|
||||||
this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed
|
this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed
|
||||||
|
|
||||||
this.isRandom = modes.isRandom;
|
this.isRandom = modes.isRandom;
|
||||||
this.chooseText = modes.chooseText;
|
this.chooseText = modes.chooseText;
|
||||||
this.allWhenKicked = modes.allWhenKicked;
|
|
||||||
this.resetEachTurn = modes.resetEachTurn;
|
this.resetEachTurn = modes.resetEachTurn;
|
||||||
this.turnNum = modes.turnNum;
|
this.turnNum = modes.turnNum;
|
||||||
if (modes.getSelectedModes().isEmpty()) {
|
if (modes.getSelectedModes().isEmpty()) {
|
||||||
|
@ -228,15 +223,6 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
this.turnNum = game.getTurnNum();
|
this.turnNum = game.getTurnNum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.allWhenKicked) {
|
|
||||||
if (KickedCondition.instance.apply(game, source)) {
|
|
||||||
this.setMinModes(0);
|
|
||||||
this.setMaxModes(3);
|
|
||||||
} else {
|
|
||||||
this.setMinModes(1);
|
|
||||||
this.setMaxModes(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.size() > 1) {
|
if (this.size() > 1) {
|
||||||
this.clearSelectedModes();
|
this.clearSelectedModes();
|
||||||
if (this.isRandom) {
|
if (this.isRandom) {
|
||||||
|
@ -244,15 +230,18 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId());
|
this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if mode modifying abilities exist
|
// check if mode modifying abilities exist
|
||||||
Card card = game.getCard(source.getSourceId());
|
Card card = game.getCard(source.getSourceId());
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
for (Ability modeModifyingAbility : card.getAbilities()) {
|
for (Ability modeModifyingAbility : card.getAbilities(game)) {
|
||||||
if (modeModifyingAbility instanceof OptionalAdditionalModeSourceCosts) {
|
if (modeModifyingAbility instanceof OptionalAdditionalModeSourceCosts) {
|
||||||
|
// cost must check activation conditional in changeModes
|
||||||
((OptionalAdditionalModeSourceCosts) modeModifyingAbility).changeModes(source, game);
|
((OptionalAdditionalModeSourceCosts) modeModifyingAbility).changeModes(source, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if all modes can be activated automatically
|
// check if all modes can be activated automatically
|
||||||
if (this.size() == this.getMinModes() && !isEachModeMoreThanOnce()) {
|
if (this.size() == this.getMinModes() && !isEachModeMoreThanOnce()) {
|
||||||
Set<UUID> onceSelectedModes = null;
|
Set<UUID> onceSelectedModes = null;
|
||||||
|
@ -434,8 +423,6 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (this.chooseText != null) {
|
if (this.chooseText != null) {
|
||||||
sb.append(chooseText);
|
sb.append(chooseText);
|
||||||
} else if (this.allWhenKicked) {
|
|
||||||
sb.append("choose one. If this spell was kicked, choose any number instead.");
|
|
||||||
} else if (this.getMaxModesFilter() != null) {
|
} else if (this.getMaxModesFilter() != null) {
|
||||||
sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage());
|
sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage());
|
||||||
} else if (this.getMinModes() == 0 && this.getMaxModes() == 1) {
|
} else if (this.getMinModes() == 0 && this.getMaxModes() == 1) {
|
||||||
|
@ -499,22 +486,10 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||||
this.eachModeMoreThanOnce = eachModeMoreThanOnce;
|
this.eachModeMoreThanOnce = eachModeMoreThanOnce;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OptionalAdditionalModeSourceCosts getAdditionalCost() {
|
|
||||||
return optionalAdditionalModeSourceCosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAdditionalCost(OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts) {
|
|
||||||
this.optionalAdditionalModeSourceCosts = optionalAdditionalModeSourceCosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRandom(boolean isRandom) {
|
public void setRandom(boolean isRandom) {
|
||||||
this.isRandom = isRandom;
|
this.isRandom = isRandom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllWhenKicked(boolean allWhenKicked) {
|
|
||||||
this.allWhenKicked = allWhenKicked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isResetEachTurn() {
|
public boolean isResetEachTurn() {
|
||||||
return resetEachTurn;
|
return resetEachTurn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities.costs;
|
package mage.abilities.costs;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -161,5 +160,4 @@ public class CostsImpl<T extends Cost> extends ArrayList<T> implements Costs<T>
|
||||||
public Costs<T> copy() {
|
public Costs<T> copy() {
|
||||||
return new CostsImpl(this);
|
return new CostsImpl(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
|
||||||
package mage.abilities.costs;
|
package mage.abilities.costs;
|
||||||
|
|
||||||
|
import mage.util.Copyable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public interface OptionalAdditionalCost extends Cost {
|
public interface OptionalAdditionalCost extends Cost, Copyable<OptionalAdditionalCost> {
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
|
@ -29,20 +30,18 @@ public interface OptionalAdditionalCost extends Cost {
|
||||||
* message.
|
* message.
|
||||||
*
|
*
|
||||||
* @param position - if there are multiple costs, it's the postion the cost
|
* @param position - if there are multiple costs, it's the postion the cost
|
||||||
* is set (starting with 0)
|
* is set (starting with 0)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String getCastSuffixMessage(int position);
|
String getCastSuffixMessage(int position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the player intends to pay the cost, the cost will be activated
|
* If the player intends to pay the cost, the cost will be activated
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
void activate();
|
void activate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the activate and count information
|
* Reset the activate and count information
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
|
|
||||||
package mage.abilities.costs;
|
package mage.abilities.costs;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Cost that can change ability's modes (example: Kicker or Entwine can make all modes selectabled).
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public interface OptionalAdditionalModeSourceCosts {
|
public interface OptionalAdditionalModeSourceCosts extends OptionalAdditionalSourceCosts {
|
||||||
|
|
||||||
void addOptionalAdditionalModeCosts(Ability ability, Game game);
|
|
||||||
|
|
||||||
void changeModes(Ability ability, Game game);
|
void changeModes(Ability ability, Game game);
|
||||||
|
|
||||||
String getCastMessageSuffix();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,19 @@ package mage.abilities.keyword;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.StaticAbility;
|
import mage.abilities.StaticAbility;
|
||||||
import mage.abilities.costs.*;
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.OptionalAdditionalCost;
|
||||||
|
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||||
|
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.constants.AbilityType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 702.40. Entwine
|
* 702.40. Entwine
|
||||||
|
@ -24,20 +29,23 @@ import java.util.Iterator;
|
||||||
* 702.40b If the entwine cost was paid, follow the text of each of the modes in
|
* 702.40b If the entwine cost was paid, follow the text of each of the modes in
|
||||||
* the order written on the card when the spell resolves.
|
* the order written on the card when the spell resolves.
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts {
|
public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts {
|
||||||
|
|
||||||
private static final String keywordText = "Entwine";
|
private static final String keywordText = "Entwine";
|
||||||
|
protected static final String reminderText = "You may {cost} in addition to any other costs to use all modes.";
|
||||||
|
|
||||||
protected OptionalAdditionalCost additionalCost;
|
protected OptionalAdditionalCost additionalCost;
|
||||||
|
protected Set<String> activations = new HashSet<>(); // same logic as KickerAbility: activations per zoneChangeCounter
|
||||||
|
|
||||||
public EntwineAbility(String manaString) {
|
public EntwineAbility(String manaString) {
|
||||||
super(Zone.STACK, null);
|
super(Zone.STACK, null);
|
||||||
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "Choose both if you pay the entwine cost.", new ManaCostsImpl(manaString));
|
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntwineAbility(Cost cost) {
|
public EntwineAbility(Cost cost) {
|
||||||
this(cost, "Choose both if you pay the entwine cost.");
|
this(cost, reminderText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntwineAbility(Cost cost, String reminderText) {
|
public EntwineAbility(Cost cost, String reminderText) {
|
||||||
|
@ -48,7 +56,10 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
||||||
|
|
||||||
public EntwineAbility(final EntwineAbility ability) {
|
public EntwineAbility(final EntwineAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
additionalCost = ability.additionalCost;
|
if (ability.additionalCost != null) {
|
||||||
|
this.additionalCost = ability.additionalCost.copy();
|
||||||
|
}
|
||||||
|
this.activations.addAll(ability.activations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,61 +68,25 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCost(Cost cost) {
|
public void addOptionalAdditionalCosts(Ability ability, Game game) {
|
||||||
if (additionalCost != null) {
|
|
||||||
((Costs) additionalCost).add(cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActivated() {
|
|
||||||
if (additionalCost != null) {
|
|
||||||
return additionalCost.isActivated();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resetCosts() {
|
|
||||||
if (additionalCost != null) {
|
|
||||||
additionalCost.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changeModes(Ability ability, Game game) {
|
|
||||||
if (!(ability instanceof SpellAbility)) {
|
if (!(ability instanceof SpellAbility)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = game.getPlayer(ability.getControllerId());
|
Player player = game.getPlayer(ability.getControllerId());
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.resetCosts();
|
|
||||||
|
this.resetCosts(game, ability);
|
||||||
if (additionalCost == null) {
|
if (additionalCost == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
|
if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
|
||||||
&& player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
&& player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
||||||
|
addCostsToAbility(additionalCost, ability);
|
||||||
additionalCost.activate();
|
activateCost(game, ability);
|
||||||
int modeCount = ability.getModes().size();
|
|
||||||
ability.getModes().setAdditionalCost(this);
|
|
||||||
ability.getModes().setMinModes(modeCount);
|
|
||||||
ability.getModes().setMaxModes(modeCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOptionalAdditionalModeCosts(Ability ability, Game game) {
|
|
||||||
if (additionalCost.isActivated()) {
|
|
||||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
|
||||||
Cost cost = (Cost) it.next();
|
|
||||||
if (cost instanceof ManaCostsImpl) {
|
|
||||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
|
||||||
} else {
|
|
||||||
ability.getCosts().add(cost.copy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,11 +109,52 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getReminderText() {
|
public void changeModes(Ability ability, Game game) {
|
||||||
if (additionalCost != null) {
|
if (!costWasActivated(ability, game)) {
|
||||||
return additionalCost.getReminderText();
|
return;
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// activate max modes all the time
|
||||||
|
int maxModes = ability.getModes().size();
|
||||||
|
ability.getModes().setMinModes(maxModes);
|
||||||
|
ability.getModes().setMaxModes(maxModes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCostsToAbility(Cost cost, Ability ability) {
|
||||||
|
ability.addCost(cost.copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetCosts(Game game, Ability source) {
|
||||||
|
if (additionalCost != null) {
|
||||||
|
additionalCost.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = getActivationKey(source, game);
|
||||||
|
this.activations.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activateCost(Game game, Ability source) {
|
||||||
|
String key = getActivationKey(source, game);
|
||||||
|
this.activations.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean costWasActivated(Ability ability, Game game) {
|
||||||
|
String key = getActivationKey(ability, game);
|
||||||
|
return this.activations.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getActivationKey(Ability source, Game game) {
|
||||||
|
// same logic as KickerAbility, uses for source ability only
|
||||||
|
int zcc = 0;
|
||||||
|
if (source.getAbilityType() == AbilityType.TRIGGERED) {
|
||||||
|
zcc = source.getSourceObjectZoneChangeCounter();
|
||||||
|
}
|
||||||
|
if (zcc == 0) {
|
||||||
|
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
|
||||||
|
}
|
||||||
|
if (zcc > 0 && (source.getAbilityType() == AbilityType.TRIGGERED)) {
|
||||||
|
--zcc;
|
||||||
|
}
|
||||||
|
return String.valueOf(zcc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
||||||
public KickerAbility(final KickerAbility ability) {
|
public KickerAbility(final KickerAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
for (OptionalAdditionalCost cost : ability.kickerCosts) {
|
for (OptionalAdditionalCost cost : ability.kickerCosts) {
|
||||||
this.kickerCosts.add((OptionalAdditionalCost) cost.copy());
|
this.kickerCosts.add(cost.copy());
|
||||||
}
|
}
|
||||||
this.keywordText = ability.keywordText;
|
this.keywordText = ability.keywordText;
|
||||||
this.reminderText = ability.reminderText;
|
this.reminderText = ability.reminderText;
|
||||||
|
@ -113,7 +113,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
||||||
cost.reset();
|
cost.reset();
|
||||||
}
|
}
|
||||||
String key = getActivationKey(source, "", game);
|
String key = getActivationKey(source, "", game);
|
||||||
for (Iterator<String> iterator = activations.keySet().iterator(); iterator.hasNext();) {
|
for (Iterator<String> iterator = activations.keySet().iterator(); iterator.hasNext(); ) {
|
||||||
String activationKey = iterator.next();
|
String activationKey = iterator.next();
|
||||||
if (activationKey.startsWith(key)
|
if (activationKey.startsWith(key)
|
||||||
&& activations.get(activationKey) > 0) {
|
&& activations.get(activationKey) > 0) {
|
||||||
|
@ -192,15 +192,15 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
||||||
// canPay checks only single mana available, not total mana usage
|
// canPay checks only single mana available, not total mana usage
|
||||||
if (kickerCost.canPay(ability, sourceId, ability.getControllerId(), game)
|
if (kickerCost.canPay(ability, sourceId, ability.getControllerId(), game)
|
||||||
&& player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt,
|
&& player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt,
|
||||||
"Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
|
"Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
|
||||||
this.activateKicker(kickerCost, ability, game);
|
this.activateKicker(kickerCost, ability, game);
|
||||||
if (kickerCost instanceof Costs) {
|
if (kickerCost instanceof Costs) {
|
||||||
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) {
|
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) {
|
||||||
Object kickerCostObject = itKickerCost.next();
|
Object kickerCostObject = itKickerCost.next();
|
||||||
if ((kickerCostObject instanceof Costs)
|
if ((kickerCostObject instanceof Costs)
|
||||||
|| (kickerCostObject instanceof CostsImpl)) {
|
|| (kickerCostObject instanceof CostsImpl)) {
|
||||||
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails
|
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails
|
||||||
= ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) {
|
= ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) {
|
||||||
addKickerCostsToAbility(itDetails.next(), ability, game);
|
addKickerCostsToAbility(itDetails.next(), ability, game);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||||
|
import mage.game.Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as KickerAbility, but can enable any number modes in spell ability
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class KickerWithAnyNumberModesAbility extends KickerAbility implements OptionalAdditionalModeSourceCosts {
|
||||||
|
|
||||||
|
public KickerWithAnyNumberModesAbility(Cost cost) {
|
||||||
|
super(cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KickerWithAnyNumberModesAbility(final KickerWithAnyNumberModesAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeModes(Ability ability, Game game) {
|
||||||
|
if (!isKicked(game, ability, "")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// activate any number modes
|
||||||
|
int maxModes = ability.getModes().size();
|
||||||
|
ability.getModes().setMinModes(0);
|
||||||
|
ability.getModes().setMaxModes(maxModes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KickerWithAnyNumberModesAbility copy() {
|
||||||
|
return new KickerWithAnyNumberModesAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import mage.abilities.condition.common.KickedCondition;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.OptionalAdditionalCost;
|
import mage.abilities.costs.OptionalAdditionalCost;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue