mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +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.common.FightTargetsEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
@ -33,12 +33,11 @@ public final class InscriptionOfAbundance extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{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.
|
||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
||||
|
||||
// • 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().addTarget(new TargetCreaturePermanent());
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.ReturnToHandTargetEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
@ -29,12 +29,11 @@ public final class InscriptionOfInsight extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{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.
|
||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
||||
|
||||
// • 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().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.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardTargetEffect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.abilities.keyword.KickerWithAnyNumberModesAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
@ -40,12 +40,11 @@ public final class InscriptionOfRuin extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{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.
|
||||
this.getSpellAbility().getModes().setAllWhenKicked(true);
|
||||
|
||||
// • 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().addTarget(new TargetOpponent());
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,39 +6,172 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
|
||||
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
|
||||
public void test_ToothAndNail() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
|
||||
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Pillarfield Ox", 1);
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 9);
|
||||
// Choose one -
|
||||
// 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.
|
||||
// Entwine {2}
|
||||
addCard(Zone.HAND, playerA, "Tooth and Nail"); // Sorcery {5}{G}{G}
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tooth and Nail");
|
||||
setChoice(playerA, "Yes"); // Message: Pay Entwine {2} ?
|
||||
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||
|
||||
setChoice(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||
|
||||
setChoice(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertAllCommandsUsed();
|
||||
|
||||
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||
assertPermanentCount(playerA, "Pillarfield Ox", 1);
|
||||
}
|
||||
|
|
|
@ -400,6 +400,7 @@ public class KickerTest extends CardTestPlayerBase {
|
|||
assertTappedCount("Swamp", true, 5);
|
||||
assertGraveyardCount(playerA, "Marsh Casualties", 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
|
||||
* Commander game against the Computer, if that helps.
|
||||
*
|
||||
* <p>
|
||||
* Edit: It's not letting me cast fused spells for free. Others seems to be
|
||||
* 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.
|
||||
// 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
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bog Wraith", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning", "Bog Wraith");
|
||||
addTarget(playerA, playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning");
|
||||
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();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Barbed Lightning", 1);
|
||||
assertGraveyardCount(playerB, "Bog Wraith", 1);
|
||||
|
@ -293,7 +300,7 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
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;
|
||||
|
||||
import mage.abilities.keyword.SwampwalkAbility;
|
||||
|
@ -8,7 +7,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ChooseOneTest extends CardTestPlayerBase {
|
||||
|
@ -48,8 +46,10 @@ public class ChooseOneTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion");
|
||||
setModeChoice(playerA, "2");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Funeral Charm", 1);
|
||||
assertPowerToughness(playerB, "Silvercoat Lion", 4, 1);
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageIdentifier;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.costs.*;
|
||||
|
@ -36,6 +32,11 @@ import mage.util.ThreadLocalStringBuilder;
|
|||
import mage.watchers.Watcher;
|
||||
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
|
||||
*/
|
||||
|
@ -227,22 +228,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
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);
|
||||
if (getSourceObjectZoneChangeCounter() == 0) {
|
||||
setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId()));
|
||||
}
|
||||
if (controller.isTestMode()) {
|
||||
if (!controller.addTargets(this, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 20130201 - 601.2b
|
||||
* 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
|
||||
// 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
|
||||
|
@ -311,8 +297,29 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
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()) {
|
||||
this.getModes().setActiveMode(modeId);
|
||||
|
||||
//20121001 - 601.2c
|
||||
// 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
|
||||
|
@ -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
|
||||
sourceObject.adjustTargets(this, game);
|
||||
}
|
||||
|
||||
if (!getTargets().isEmpty()) {
|
||||
Outcome outcome = getEffects().getOutcome(this);
|
||||
|
||||
// only activated abilities can be canceled by human user (not triggered)
|
||||
boolean canCancel = this instanceof ActivatedAbility && controller.isHuman();
|
||||
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, canCancel)) {
|
||||
|
@ -434,7 +443,11 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
boolean alternativeCostUsed = false;
|
||||
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);
|
||||
|
||||
// 1. ALTERNATIVE COSTS
|
||||
for (Ability ability : abilities) {
|
||||
// if cast for noMana no Alternative costs are allowed
|
||||
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
|
||||
if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
|
||||
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;
|
||||
|
@ -806,7 +822,18 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
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) {
|
||||
this.addManaCost((ManaCost) cost);
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.abilities;
|
||||
|
||||
import mage.abilities.condition.common.KickedCondition;
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
|
@ -33,11 +32,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
private TargetController modeChooser;
|
||||
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 OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid
|
||||
private Filter maxModesFilter = null; // calculates the max number of available modes
|
||||
private boolean isRandom = false;
|
||||
private String chooseText = null;
|
||||
private boolean allWhenKicked = false;
|
||||
private boolean resetEachTurn = false;
|
||||
private int turnNum = 0;
|
||||
|
||||
|
@ -68,12 +65,10 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.modeChooser = modes.modeChooser;
|
||||
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
||||
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
||||
this.optionalAdditionalModeSourceCosts = modes.optionalAdditionalModeSourceCosts;
|
||||
this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed
|
||||
|
||||
this.isRandom = modes.isRandom;
|
||||
this.chooseText = modes.chooseText;
|
||||
this.allWhenKicked = modes.allWhenKicked;
|
||||
this.resetEachTurn = modes.resetEachTurn;
|
||||
this.turnNum = modes.turnNum;
|
||||
if (modes.getSelectedModes().isEmpty()) {
|
||||
|
@ -228,15 +223,6 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
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) {
|
||||
this.clearSelectedModes();
|
||||
if (this.isRandom) {
|
||||
|
@ -244,15 +230,18 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if mode modifying abilities exist
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability modeModifyingAbility : card.getAbilities()) {
|
||||
for (Ability modeModifyingAbility : card.getAbilities(game)) {
|
||||
if (modeModifyingAbility instanceof OptionalAdditionalModeSourceCosts) {
|
||||
// cost must check activation conditional in changeModes
|
||||
((OptionalAdditionalModeSourceCosts) modeModifyingAbility).changeModes(source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if all modes can be activated automatically
|
||||
if (this.size() == this.getMinModes() && !isEachModeMoreThanOnce()) {
|
||||
Set<UUID> onceSelectedModes = null;
|
||||
|
@ -434,8 +423,6 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
if (this.chooseText != null) {
|
||||
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) {
|
||||
sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage());
|
||||
} else if (this.getMinModes() == 0 && this.getMaxModes() == 1) {
|
||||
|
@ -499,22 +486,10 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.eachModeMoreThanOnce = eachModeMoreThanOnce;
|
||||
}
|
||||
|
||||
public OptionalAdditionalModeSourceCosts getAdditionalCost() {
|
||||
return optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
public void setAdditionalCost(OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts) {
|
||||
this.optionalAdditionalModeSourceCosts = optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
public void setRandom(boolean isRandom) {
|
||||
this.isRandom = isRandom;
|
||||
}
|
||||
|
||||
public void setAllWhenKicked(boolean allWhenKicked) {
|
||||
this.allWhenKicked = allWhenKicked;
|
||||
}
|
||||
|
||||
public boolean isResetEachTurn() {
|
||||
return resetEachTurn;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -161,5 +160,4 @@ public class CostsImpl<T extends Cost> extends ArrayList<T> implements Costs<T>
|
|||
public Costs<T> copy() {
|
||||
return new CostsImpl(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import mage.util.Copyable;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface OptionalAdditionalCost extends Cost {
|
||||
public interface OptionalAdditionalCost extends Cost, Copyable<OptionalAdditionalCost> {
|
||||
|
||||
String getName();
|
||||
|
||||
|
@ -29,20 +30,18 @@ public interface OptionalAdditionalCost extends Cost {
|
|||
* message.
|
||||
*
|
||||
* @param position - if there are multiple costs, it's the postion the cost
|
||||
* is set (starting with 0)
|
||||
* is set (starting with 0)
|
||||
* @return
|
||||
*/
|
||||
String getCastSuffixMessage(int position);
|
||||
|
||||
/**
|
||||
* If the player intends to pay the cost, the cost will be activated
|
||||
*
|
||||
*/
|
||||
void activate();
|
||||
|
||||
/**
|
||||
* Reset the activate and count information
|
||||
*
|
||||
*/
|
||||
void reset();
|
||||
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Cost that can change ability's modes (example: Kicker or Entwine can make all modes selectabled).
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface OptionalAdditionalModeSourceCosts {
|
||||
|
||||
void addOptionalAdditionalModeCosts(Ability ability, Game game);
|
||||
public interface OptionalAdditionalModeSourceCosts extends OptionalAdditionalSourceCosts {
|
||||
|
||||
void changeModes(Ability ability, Game game);
|
||||
|
||||
String getCastMessageSuffix();
|
||||
}
|
||||
|
|
|
@ -3,14 +3,19 @@ package mage.abilities.keyword;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
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.constants.AbilityType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the order written on the card when the spell resolves.
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts {
|
||||
|
||||
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 Set<String> activations = new HashSet<>(); // same logic as KickerAbility: activations per zoneChangeCounter
|
||||
|
||||
public EntwineAbility(String manaString) {
|
||||
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) {
|
||||
this(cost, "Choose both if you pay the entwine cost.");
|
||||
this(cost, reminderText);
|
||||
}
|
||||
|
||||
public EntwineAbility(Cost cost, String reminderText) {
|
||||
|
@ -48,7 +56,10 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
|
||||
public EntwineAbility(final EntwineAbility ability) {
|
||||
super(ability);
|
||||
additionalCost = ability.additionalCost;
|
||||
if (ability.additionalCost != null) {
|
||||
this.additionalCost = ability.additionalCost.copy();
|
||||
}
|
||||
this.activations.addAll(ability.activations);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,61 +68,25 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addCost(Cost cost) {
|
||||
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) {
|
||||
public void addOptionalAdditionalCosts(Ability ability, Game game) {
|
||||
if (!(ability instanceof SpellAbility)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(ability.getControllerId());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
this.resetCosts();
|
||||
|
||||
this.resetCosts(game, ability);
|
||||
if (additionalCost == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
|
||||
&& player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
||||
|
||||
additionalCost.activate();
|
||||
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());
|
||||
}
|
||||
}
|
||||
addCostsToAbility(additionalCost, ability);
|
||||
activateCost(game, ability);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,11 +109,52 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
}
|
||||
}
|
||||
|
||||
public String getReminderText() {
|
||||
if (additionalCost != null) {
|
||||
return additionalCost.getReminderText();
|
||||
} else {
|
||||
return "";
|
||||
public void changeModes(Ability ability, Game game) {
|
||||
if (!costWasActivated(ability, game)) {
|
||||
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) {
|
||||
super(ability);
|
||||
for (OptionalAdditionalCost cost : ability.kickerCosts) {
|
||||
this.kickerCosts.add((OptionalAdditionalCost) cost.copy());
|
||||
this.kickerCosts.add(cost.copy());
|
||||
}
|
||||
this.keywordText = ability.keywordText;
|
||||
this.reminderText = ability.reminderText;
|
||||
|
@ -113,7 +113,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
cost.reset();
|
||||
}
|
||||
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();
|
||||
if (activationKey.startsWith(key)
|
||||
&& 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
|
||||
if (kickerCost.canPay(ability, sourceId, ability.getControllerId(), game)
|
||||
&& 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);
|
||||
if (kickerCost instanceof Costs) {
|
||||
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) {
|
||||
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) {
|
||||
Object kickerCostObject = itKickerCost.next();
|
||||
if ((kickerCostObject instanceof Costs)
|
||||
|| (kickerCostObject instanceof CostsImpl)) {
|
||||
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails
|
||||
= ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) {
|
||||
= ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) {
|
||||
addKickerCostsToAbility(itDetails.next(), ability, game);
|
||||
}
|
||||
} 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;
|
||||
|
||||
import mage.abilities.condition.common.KickedCondition;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
|
||||
|
|
Loading…
Reference in a new issue