mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
parent
10cf884923
commit
708b4e872a
2 changed files with 311 additions and 118 deletions
|
@ -1,145 +1,298 @@
|
|||
|
||||
|
||||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
public class ConvokeTest extends CardTestPlayerBase {
|
||||
|
||||
/*
|
||||
Test are set to Ignore because the new way to handle this alternate mana payment methods
|
||||
are not supported yet from AI and getPlayable logic.
|
||||
*/
|
||||
public class ConvokeTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testConvokeTwoCreatures() {
|
||||
/**
|
||||
* Ephemeral Shields {1}{W}
|
||||
* Instant
|
||||
* Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
|
||||
* Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
|
||||
*/
|
||||
public void test_Playable_NoMana_NoConvoke() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account
|
||||
|
||||
checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Playable_Mana_NoConvoke() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Playable_NoMana_Convoke() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 4); // convoke pay
|
||||
|
||||
checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Playable_Mana_Convoke() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
|
||||
|
||||
checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Playable_ManaPartly_ConvokePartly() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2 - 1); // convoke pay
|
||||
|
||||
checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayConvoke_Manual() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
|
||||
|
||||
// use special action to pay (need disabled auto-payment and prepared mana pool)
|
||||
disableManaAutoPayment(playerA);
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
|
||||
setChoice(playerA, "Red"); // pay 1
|
||||
setChoice(playerA, "Red"); // pay 2
|
||||
setChoice(playerA, "Convoke");
|
||||
addTarget(playerA, "Goblin Racketeer"); // pay 3 as convoke
|
||||
setChoice(playerA, "Convoke");
|
||||
addTarget(playerA, "Goblin Racketeer"); // pay 4 as convoke
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayConvoke_AI_AutoPay() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
|
||||
|
||||
// AI must use special actions to pay as convoke
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
|
||||
|
||||
//setStrictChooseMode(true); AI must choose targets
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayConvoke_AI_AutoPayAsConvoke() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
|
||||
|
||||
// AI must use special actions to pay as convoke
|
||||
// Current version uses special mana pay as last, after no normal mana available (it can be changed in the future, see playManaHandling)
|
||||
// e.g. it must tap lands 2 times and convoke 2 times
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
|
||||
addTarget(playerA, "Goblin Racketeer"); // pay 1 as convoke
|
||||
addTarget(playerA, "Goblin Racketeer"); // pay 2 as convoke
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayConvoke_AI_FullPlay() {
|
||||
// {2}{R}{R}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
|
||||
// Stoke the Flames deals 4 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
|
||||
|
||||
// AI must use special actions to pay as convoke and play card
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Other_ConvokeTwoCreatures() {
|
||||
// {1}{W}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
|
||||
// Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
|
||||
addCard(Zone.HAND, playerA, "Ephemeral Shields");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Oreskos Swiftclaw", 1);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Ephemeral Shields");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
|
||||
|
||||
// AI automaticly use convoke to pay
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, "Silvercoat Lion^Oreskos Swiftclaw");
|
||||
addTarget(playerA, "Silvercoat Lion"); // pay 1 as convoke
|
||||
addTarget(playerA, "Oreskos Swiftclaw"); // pay 2 as convoke
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||
|
||||
assertGraveyardCount(playerA, "Ephemeral Shields", 1);
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||
assertPermanentCount(playerA, "Oreskos Swiftclaw", 1);
|
||||
|
||||
for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) {
|
||||
Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testConvokeTwoCreaturesOneWithProtection() {
|
||||
/**
|
||||
* Ephemeral Shields {1}{W}
|
||||
* Instant
|
||||
* Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
|
||||
* Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
|
||||
*/
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account
|
||||
|
||||
public void test_Other_ConvokeProtection() {
|
||||
// {1}{W}
|
||||
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
|
||||
// Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
|
||||
addCard(Zone.HAND, playerA, "Ephemeral Shields");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
// Protection from white
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Black Knight", 1);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Ephemeral Shields");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
|
||||
|
||||
// convoke must be able to target card with protection (it's no target)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, "Silvercoat Lion^Black Knight");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
addTarget(playerA, "Black Knight");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||
|
||||
assertGraveyardCount(playerA, "Ephemeral Shields", 1);
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible
|
||||
assertPermanentCount(playerA, "Black Knight", 1);
|
||||
assertTapped("Silvercoat Lion", true);
|
||||
assertTapped("Black Knight", true);
|
||||
|
||||
for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) {
|
||||
Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testConvokeFromChiefEngineer() {
|
||||
/**
|
||||
* Chief Engineer {1}{U}
|
||||
* Creature - Vedalken, Artificer
|
||||
* Artifact spells you cast have convoke.
|
||||
*/
|
||||
|
||||
// THIS TEST IS NOT FINISHED
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // creatures to use for convoek
|
||||
// TODO: fix gain ability for spells to apply in all zones instead stack only or change getPlayable to look ahead and simulate spell on stack (wtf)
|
||||
public void test_Other_ConvokeAsGains() {
|
||||
// {1}{U}
|
||||
// Artifact spells you cast have convoke.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Chief Engineer", 1);
|
||||
|
||||
addCard(Zone.HAND, playerA, "ARTIFACT TO CAST", 1);
|
||||
//
|
||||
// {2}
|
||||
addCard(Zone.HAND, playerA, "Alpha Myr", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2);
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "ARTIFACT TO CAST");
|
||||
setChoice(playerA, "Yes");
|
||||
// Chief Engineer gives convoke to Alpha Myr and xmage must see it as playable before put real spell to stack
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
|
||||
assertPermanentCount(playerA, "Alpha Myr", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// I don't know how to test it by framework - manual test works fine for HumanPlayer
|
||||
// (he get warning message and can't activate mana abilities after convoke)
|
||||
public void test_Other_CantUseConvokeBeforeManaAbilities() {
|
||||
// https://github.com/magefree/mage/issues/768
|
||||
|
||||
// {6}
|
||||
// Convoke
|
||||
addCard(Zone.HAND, playerA, "Will-Forged Golem", 1);
|
||||
//
|
||||
// {2}{G}
|
||||
// Create two 1/1 colorless Eldrazi Scion creature tokens. They have “Sacrifice this creature: Add {C}.”
|
||||
addCard(Zone.HAND, playerA, "Call the Scions", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3 * 2);
|
||||
|
||||
// prepare scions
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
checkPermanentCount("scions", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eldrazi Scion", 4);
|
||||
|
||||
// test case 1 - playable abilities must not show it as playable (not work, cause we don't known real payment order before payment)
|
||||
//checkPlayableAbility("can't use convoke", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem", false);
|
||||
|
||||
// test case 2 - it's in playable list, but mana abilities can't be activated after convoke pay
|
||||
//castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpecialAction;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.ActivationManaAbilityStep;
|
||||
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.AbilityType;
|
||||
|
@ -19,62 +19,67 @@ import mage.constants.ManaType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
/*
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 502.46. Convoke
|
||||
*
|
||||
* <p>
|
||||
* 502.46a Convoke is a static ability that functions while the spell is on the stack. "Convoke"
|
||||
* means "As an additional cost to play this spell, you may tap any number of untapped creatures
|
||||
* you control. Each creature tapped this way reduces the cost to play this spell by {1} or by
|
||||
* one mana of any of that creature's colors." Using the convoke ability follows the rules for
|
||||
* paying additional costs in rules 409.1b and 4091f-h.
|
||||
*
|
||||
* <p>
|
||||
* Example: You play Guardian of Vitu-Ghazi, a spell with convoke that costs {3}{G}{W}. You announce
|
||||
* that you're going to tap an artifact creature, a red creature, and a green-and-white creature to
|
||||
* help pay for it. The artifact creature and the red creature each reduce the spell's cost by {1}.
|
||||
* You choose whether the green-white creature reduces the spell's cost by {1}, {G}, or {W}. Then
|
||||
* the creatures become tapped as you pay Guardian of Vitu-Ghazi's cost.
|
||||
*
|
||||
* <p>
|
||||
* 502.46b Convoke can't reduce the cost to play a spell to less than 0.
|
||||
*
|
||||
* <p>
|
||||
* 502.46c Multiple instances of convoke on the same spell are redundant.
|
||||
*
|
||||
* <p>
|
||||
* You can tap only untapped creatures you control to reduce the cost of a spell with convoke
|
||||
* that you play.
|
||||
*
|
||||
* <p>
|
||||
* While playing a spell with convoke, if you control a creature that taps to produce mana, you
|
||||
* can either tap it for mana or tap it to reduce the cost of the spell, but not both.
|
||||
*
|
||||
* <p>
|
||||
* If you tap a multicolored creature to reduce the cost of a spell with convoke, you reduce
|
||||
* the cost by {1} or by one mana of your choice of any of that creature's colors.
|
||||
*
|
||||
* <p>
|
||||
* Convoke doesn't change a spell's mana cost or converted mana cost.
|
||||
*
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
public class ConvokeAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
|
||||
|
||||
private static final FilterCreaturePermanent filterUntapped = new FilterCreaturePermanent();
|
||||
private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent();
|
||||
|
||||
static {
|
||||
filterUntapped.add(Predicates.not(TappedPredicate.instance));
|
||||
}
|
||||
|
||||
public ConvokeAbility() {
|
||||
super(Zone.STACK, null);
|
||||
super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
|
||||
this.setRuleAtTheTop(true);
|
||||
this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped)));
|
||||
}
|
||||
|
||||
public ConvokeAbility(final ConvokeAbility ability) {
|
||||
|
@ -86,14 +91,25 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
|
|||
return new ConvokeAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Convoke <i>(Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)</i>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationManaAbilityStep useOnActivationManaAbilityStep() {
|
||||
return ActivationManaAbilityStep.AFTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) {
|
||||
if (source.getAbilityType() == AbilityType.SPELL) {
|
||||
SpecialAction specialAction = new ConvokeSpecialAction(unpaid);
|
||||
SpecialAction specialAction = new ConvokeSpecialAction(unpaid, this);
|
||||
specialAction.setControllerId(source.getControllerId());
|
||||
specialAction.setSourceId(source.getSourceId());
|
||||
|
||||
// create filter for possible creatures to tap
|
||||
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
|
||||
filter.add(Predicates.not(TappedPredicate.instance));
|
||||
|
@ -117,7 +133,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
|
|||
filter.add(Predicates.or(colorPredicates));
|
||||
}
|
||||
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
|
||||
target.setTargetName("creature to convoke");
|
||||
target.setTargetName("tap creature card as convoke's pay");
|
||||
specialAction.addTarget(target);
|
||||
if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
|
||||
game.getState().getSpecialActions().add(specialAction);
|
||||
|
@ -127,15 +143,36 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Convoke <i>(Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)</i>";
|
||||
public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
|
||||
ManaOptions options = new ManaOptions();
|
||||
FilterControlledCreaturePermanent filterBasic = new FilterControlledCreaturePermanent();
|
||||
|
||||
// each creature can give {1} or color mana
|
||||
game.getBattlefield().getActivePermanents(filterBasic, source.getControllerId(), source.getSourceId(), game)
|
||||
.stream()
|
||||
.filter(permanent -> !permanent.isTapped())
|
||||
.forEach(permanent -> {
|
||||
ManaOptions permMana = new ManaOptions();
|
||||
permMana.add(Mana.GenericMana(1));
|
||||
for (ObjectColor color : permanent.getColor(game).getColors()) {
|
||||
if (color.isBlack()) permMana.add(Mana.BlackMana(1));
|
||||
if (color.isBlue()) permMana.add(Mana.BlueMana(1));
|
||||
if (color.isGreen()) permMana.add(Mana.GreenMana(1));
|
||||
if (color.isRed()) permMana.add(Mana.RedMana(1));
|
||||
if (color.isWhite()) permMana.add(Mana.WhiteMana(1));
|
||||
}
|
||||
options.addMana(permMana);
|
||||
});
|
||||
|
||||
options.removeDuplicated();
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
class ConvokeSpecialAction extends SpecialAction {
|
||||
|
||||
public ConvokeSpecialAction(ManaCost unpaid) {
|
||||
super(Zone.ALL, true);
|
||||
public ConvokeSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) {
|
||||
super(Zone.ALL, manaAbility);
|
||||
setRuleVisible(false);
|
||||
this.addEffect(new ConvokeEffect(unpaid));
|
||||
}
|
||||
|
@ -157,7 +194,7 @@ class ConvokeEffect extends OneShotEffect {
|
|||
public ConvokeEffect(ManaCost unpaid) {
|
||||
super(Outcome.Benefit);
|
||||
this.unpaid = unpaid;
|
||||
this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {C} or one mana of that creature's color.)";
|
||||
this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)";
|
||||
}
|
||||
|
||||
public ConvokeEffect(final ConvokeEffect effect) {
|
||||
|
@ -173,7 +210,8 @@ class ConvokeEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Spell spell = game.getStack().getSpell(source.getSourceId());
|
||||
if (controller != null && spell != null) {
|
||||
for (UUID creatureId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent perm = game.getPermanent(creatureId);
|
||||
if (perm == null) {
|
||||
|
@ -225,8 +263,10 @@ class ConvokeEffect extends OneShotEffect {
|
|||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CONVOKED, perm.getId(), source.getSourceId(), source.getControllerId()));
|
||||
game.informPlayers("Convoke: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay one " + manaName + " mana");
|
||||
}
|
||||
|
||||
// can't use mana abilities after that (convoke cost must be payed after mana abilities only)
|
||||
spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue