Reworked Convoke ability (#768, #6636)

This commit is contained in:
Oleg Agafonov 2020-06-19 13:19:27 +04:00
parent 10cf884923
commit 708b4e872a
2 changed files with 311 additions and 118 deletions

View file

@ -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 creatures 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 creatures 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 creatures 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 creatures 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 creatures 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 creatures 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 creatures 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 creatures 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 creatures 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();
}
}

View file

@ -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;
}