Some changes/fixes to conspire ability.

This commit is contained in:
LevelX2 2015-10-03 13:33:22 +02:00
parent 8e50c18d8c
commit 7b68604471
14 changed files with 234 additions and 128 deletions

View file

@ -58,7 +58,7 @@ public class AEthertow extends CardImpl {
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }
public AEthertow(final AEthertow card) { public AEthertow(final AEthertow card) {

View file

@ -51,7 +51,7 @@ public class BarkshellBlessing extends CardImpl {
this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn));
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }
public BarkshellBlessing(final BarkshellBlessing card) { public BarkshellBlessing(final BarkshellBlessing card) {

View file

@ -50,7 +50,7 @@ public class BurnTrail extends CardImpl {
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }
public BurnTrail(final BurnTrail card) { public BurnTrail(final BurnTrail card) {

View file

@ -51,7 +51,7 @@ public class DisturbingPlot extends CardImpl {
this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard")));
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }

View file

@ -52,7 +52,7 @@ public class GhastlyDiscovery extends CardImpl {
this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect()); this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect());
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE));
} }
public GhastlyDiscovery(final GhastlyDiscovery card) { public GhastlyDiscovery(final GhastlyDiscovery card) {

View file

@ -58,7 +58,7 @@ public class Giantbaiting extends CardImpl {
this.getSpellAbility().addEffect(new GiantbaitingEffect()); this.getSpellAbility().addEffect(new GiantbaitingEffect());
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE));
} }

View file

@ -51,7 +51,7 @@ public class GleefulSabotage extends CardImpl {
this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()));
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }
public GleefulSabotage(final GleefulSabotage card) { public GleefulSabotage(final GleefulSabotage card) {

View file

@ -50,7 +50,7 @@ public class MemorySluice extends CardImpl {
this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new TargetPlayer());
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }

View file

@ -60,7 +60,7 @@ public class MineExcavation extends CardImpl {
this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter));
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }
public MineExcavation(final MineExcavation card) { public MineExcavation(final MineExcavation card) {

View file

@ -64,7 +64,7 @@ public class TraitorsRoar extends CardImpl {
this.getSpellAbility().addEffect(new TraitorsRoarEffect()); this.getSpellAbility().addEffect(new TraitorsRoarEffect());
// Conspire // Conspire
this.addAbility(new ConspireAbility(this)); this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
} }

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package org.mage.test.cards.abilities.keywords; package org.mage.test.cards.abilities.keywords;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
@ -40,31 +39,31 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class ConspireTest extends CardTestPlayerBase { public class ConspireTest extends CardTestPlayerBase {
/** /**
* 702.77. Conspire * 702.77. Conspire 702.77a Conspire is a keyword that represents two
* 702.77a Conspire is a keyword that represents two abilities. The first is a static ability that functions * abilities. The first is a static ability that functions while the spell
* while the spell with conspire is on the stack. The second is a triggered ability that functions * with conspire is on the stack. The second is a triggered ability that
* while the spell with conspire is on the stack. Conspire means As an additional cost to cast * functions while the spell with conspire is on the stack. Conspire means
* this spell, you may tap two untapped creatures you control that each share a color with it and * As an additional cost to cast this spell, you may tap two untapped
* When you cast this spell, if its conspire cost was paid, copy it. If the spell has any targets, you * creatures you control that each share a color with it and When you cast
* may choose new targets for the copy. Paying a spells conspire cost follows the rules for * this spell, if its conspire cost was paid, copy it. If the spell has any
* paying additional costs in rules 601.2b and 601.2eg. * targets, you may choose new targets for the copy. Paying a spells
* conspire cost follows the rules for paying additional costs in rules
* 601.2b and 601.2eg.
* *
* 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers based on * 702.77b If a spell has multiple instances of conspire, each is paid
* its own payment, not any other instance of conspire * separately and triggers based on its own payment, not any other instance
* of conspire
* *
*/ */
/** /**
* Burn Trail * Burn Trail Sorcery, 3R (4) Burn Trail deals 3 damage to target creature
* Sorcery, 3R (4) * or player.
* Burn Trail deals 3 damage to target creature or player.
* *
* Conspire (As you cast this spell, you may tap two untapped creatures you * Conspire (As you cast this spell, you may tap two untapped creatures you
* control that share a color with it. When you do, copy it and you may * control that share a color with it. When you do, copy it and you may
* choose a new target for the copy.) * choose a new target for the copy.)
* *
*/ */
@Test @Test
public void testConspire() { public void testConspire() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
@ -72,7 +71,6 @@ public class ConspireTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin");
addCard(Zone.HAND, playerA, "Burn Trail"); addCard(Zone.HAND, playerA, "Burn Trail");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB);
setChoice(playerA, "Yes"); setChoice(playerA, "Yes");
@ -93,7 +91,6 @@ public class ConspireTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin");
addCard(Zone.HAND, playerA, "Burn Trail"); addCard(Zone.HAND, playerA, "Burn Trail");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB);
setChoice(playerA, "No"); setChoice(playerA, "No");
@ -107,4 +104,50 @@ public class ConspireTest extends CardTestPlayerBase {
} }
@Test
public void testWortTheRaidmother() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7);
// When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield.
// Each red or green instant or sorcery spell you cast has conspire.
// (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)
addCard(Zone.HAND, playerA, "Wort, the Raidmother");
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother");// {4}{R/G}{R/G}
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Wort, the Raidmother", 1);
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertLife(playerB, 14);
}
@Test
public void testWortTheRaidmotherWithConspireSpell() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10);
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2);
// When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield.
// Each red or green instant or sorcery spell you cast has conspire.
// (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)
addCard(Zone.HAND, playerA, "Wort, the Raidmother");
addCard(Zone.HAND, playerA, "Burn Trail");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G}
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Burn Trail", playerB);
setChoice(playerA, "Yes"); // use Conspire from Burn Trail itself
setChoice(playerA, "Yes"); // use Conspire gained from Wort, the Raidmother
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Wort, the Raidmother", 1);
assertLife(playerB, 11);
assertLife(playerA, 20);
assertGraveyardCount(playerA, "Burn Trail", 1);
}
} }

View file

@ -445,20 +445,28 @@ public abstract class AbilityImpl implements Ability {
public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) {
boolean alternativeCostisUsed = false; boolean alternativeCostisUsed = false;
if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) { if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) {
for (Ability ability : sourceObject.getAbilities()) { Abilities<Ability> abilities = null;
// if cast for noMana no Alternative costs are allowed if (sourceObject instanceof Card) {
if (!noMana && ability instanceof AlternativeSourceCosts) { abilities = ((Card) sourceObject).getAbilities(game);
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; } else {
if (alternativeSpellCosts.isAvailable(this, game)) { sourceObject.getAbilities();
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { }
// only one alternative costs may be activated if (abilities != null) {
alternativeCostisUsed = true; for (Ability ability : abilities) {
break; // if cast for noMana no Alternative costs are allowed
if (!noMana && ability instanceof AlternativeSourceCosts) {
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
if (alternativeSpellCosts.isAvailable(this, game)) {
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
// only one alternative costs may be activated
alternativeCostisUsed = true;
break;
}
} }
} }
} if (ability instanceof OptionalAdditionalSourceCosts) {
if (ability instanceof OptionalAdditionalSourceCosts) { ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); }
} }
} }
// controller specific alternate spell costs // controller specific alternate spell costs

View file

@ -27,14 +27,16 @@
*/ */
package mage.abilities.keyword; package mage.abilities.keyword;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.SpellAbility; import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility; import mage.abilities.StaticAbility;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.Cost; import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs; import mage.abilities.costs.Costs;
import mage.abilities.costs.OptionalAdditionalCost;
import mage.abilities.costs.OptionalAdditionalCostImpl; import mage.abilities.costs.OptionalAdditionalCostImpl;
import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.OptionalAdditionalSourceCosts;
import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.common.TapTargetCost;
@ -50,48 +52,77 @@ import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetControlledPermanent;
/** /*
* 702.77. Conspire 702.77a Conspire is a keyword that represents two abilities. * 702.77. Conspire
* The first is a static ability that functions while the spell with conspire is * 702.77a Conspire is a keyword that represents two abilities.
* on the stack. The second is a triggered ability that functions while the * The first is a static ability that functions while the spell with conspire is on the stack.
* spell with conspire is on the stack. "Conspire" means "As an additional cost * The second is a triggered ability that functions while the spell with conspire is on the stack.
* to cast this spell, you may tap two untapped creatures you control that each * "Conspire" means "As an additional cost to cast this spell,
* share a color with it" and "When you cast this spell, if its conspire cost * you may tap two untapped creatures you control that each share a color with it"
* was paid, copy it. If the spell has any targets, you may choose new targets * and "When you cast this spell, if its conspire cost was paid, copy it.
* for the copy." Paying a spells conspire cost follows the rules for paying * If the spell has any targets, you may choose new targets for the copy."
* additional costs in rules 601.2b and 601.2eg. 702.77b If a spell has * Paying a spells conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2eg.
* multiple instances of conspire, each is paid separately and triggers based on * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers
* its own payment, not any other instance of conspire. * * based on its own payment, not any other instance of conspire. *
* *
* @author jeffwadsworth heavily based off the replicate keyword by LevelX * @author jeffwadsworth heavily based off the replicate keyword by LevelX
*/ */
public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
private static final String keywordText = "Conspire"; private static final String keywordText = "Conspire";
private static final String reminderTextCost = "<i>As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)</i>"; private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control that share a color with it");
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("two untapped creatures you control that share a color with it"); protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation";
static { static {
filter.add(Predicates.not(new TappedPredicate())); filter.add(Predicates.not(new TappedPredicate()));
filter.add(new SharesColorWithSourcePredicate()); filter.add(new SharesColorWithSourcePredicate());
} }
Cost costConspire = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); public enum ConspireTargets {
OptionalAdditionalCost conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, costConspire);
public ConspireAbility(Card card) { NONE,
ONE,
MORE
}
private UUID conspireId;
private String reminderText;
private OptionalAdditionalCostImpl conspireCost;
/**
* Unique Id for a ConspireAbility but may not change while a continuous
* effect gives Conspire
*
* @param conspireId
* @param conspireTargets controls the content of the reminder text
*/
public ConspireAbility(UUID conspireId, ConspireTargets conspireTargets) {
super(Zone.STACK, null); super(Zone.STACK, null);
setRuleAtTheTop(false); this.conspireId = conspireId;
addSubAbility(new ConspireTriggeredAbility()); switch (conspireTargets) {
case NONE:
reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.)";
break;
case ONE:
reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)";
break;
case MORE:
reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new targets for the copy.)";
break;
}
conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText,
new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)));
addSubAbility(new ConspireTriggeredAbility(conspireId));
} }
public ConspireAbility(final ConspireAbility ability) { public ConspireAbility(final ConspireAbility ability) {
super(ability); super(ability);
conspireCost = ability.conspireCost; this.conspireId = ability.conspireId;
this.conspireCost = ability.conspireCost.copy();
this.reminderText = ability.reminderText;
} }
@Override @Override
@ -106,18 +137,21 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
} }
} }
@Override public UUID getConspireId() {
public boolean isActivated() { return conspireId;
if (conspireCost != null) {
return conspireCost.isActivated();
}
return false;
} }
public void resetConspire() { @Override
if (conspireCost != null) { public boolean isActivated() {
conspireCost.reset(); throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!");
}
public boolean isActivated(Ability ability, Game game) {
Set<UUID> activations = (Set<UUID>) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId());
if (activations != null) {
return activations.contains(getConspireId());
} }
return false;
} }
@Override @Override
@ -125,9 +159,9 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
if (ability instanceof SpellAbility) { if (ability instanceof SpellAbility) {
Player player = game.getPlayer(controllerId); Player player = game.getPlayer(controllerId);
if (player != null) { if (player != null) {
this.resetConspire(); resetConspire(ability, game);
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), ability, game)) { if (player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) {
conspireCost.activate(); activateConspire(ability, game);
for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) { for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next(); Cost cost = (Cost) it.next();
ability.getCosts().add(cost.copy()); ability.getCosts().add(cost.copy());
@ -137,6 +171,22 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
} }
} }
private void activateConspire(Ability ability, Game game) {
Set<UUID> activations = (Set<UUID>) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId());
if (activations == null) {
activations = new HashSet<>();
game.getState().setValue(CONSPIRE_ACTIVATION_KEY + ability.getId(), activations);
}
activations.add(getConspireId());
}
private void resetConspire(Ability ability, Game game) {
Set<UUID> activations = (Set<UUID>) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId());
if (activations != null) {
activations.remove(getConspireId());
}
}
@Override @Override
public String getRule() { public String getRule() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -167,13 +217,17 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
class ConspireTriggeredAbility extends TriggeredAbilityImpl { class ConspireTriggeredAbility extends TriggeredAbilityImpl {
public ConspireTriggeredAbility() { private UUID conspireId;
public ConspireTriggeredAbility(UUID conspireId) {
super(Zone.STACK, new ConspireEffect()); super(Zone.STACK, new ConspireEffect());
this.conspireId = conspireId;
this.setRuleVisible(false); this.setRuleVisible(false);
} }
private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) {
super(ability); super(ability);
this.conspireId = ability.conspireId;
} }
@Override @Override
@ -188,20 +242,18 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl {
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(this.sourceId)) { if (event.getSourceId().equals(getSourceId())) {
StackObject spell = game.getStack().getStackObject(this.sourceId); Spell spell = game.getStack().getSpell(event.getSourceId());
if (spell instanceof Spell) { for (Ability ability : spell.getAbilities(game)) {
Card card = game.getCard(spell.getSourceId()); if (ability instanceof ConspireAbility
if (card != null) { && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) {
for (Ability ability : card.getAbilities()) { if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) {
if (ability instanceof ConspireAbility) { for (Effect effect : this.getEffects()) {
if (((ConspireAbility) ability).isActivated()) { if (effect instanceof ConspireEffect) {
for (Effect effect : this.getEffects()) { ((ConspireEffect) effect).setConspiredSpell(spell);
effect.setValue("ConspireSpell", spell);
}
return true;
} }
} }
return true;
} }
} }
} }
@ -209,52 +261,53 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl {
return false; return false;
} }
public UUID getConspireId() {
return conspireId;
}
@Override @Override
public String getRule() { public String getRule() {
return "Conspire: <i>As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)</i>"; return "When you pay the conspire costs, copy it and you may choose a new target for the copy.";
} }
} }
class ConspireEffect extends OneShotEffect { class ConspireEffect extends OneShotEffect {
private Spell conspiredSpell;
public ConspireEffect() { public ConspireEffect() {
super(Outcome.Copy); super(Outcome.Copy);
} }
public ConspireEffect(final ConspireEffect effect) { public ConspireEffect(final ConspireEffect effect) {
super(effect); super(effect);
this.conspiredSpell = effect.conspiredSpell;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller != null && conspiredSpell != null) {
Spell spell = (Spell) this.getValue("ConspireSpell"); Card card = game.getCard(conspiredSpell.getSourceId());
if (spell != null) { if (card != null) {
Card card = game.getCard(spell.getSourceId()); Spell copy = conspiredSpell.copySpell();
if (card != null) { copy.setControllerId(source.getControllerId());
for (Ability ability : card.getAbilities()) { copy.setCopiedSpell(true);
if (ability instanceof ConspireAbility) { game.getStack().push(copy);
if (((ConspireAbility) ability).isActivated()) { copy.chooseNewTargets(game, source.getControllerId());
((ConspireAbility) ability).resetConspire(); if (!game.isSimulation()) {
} game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game));
}
}
Spell copy = spell.copySpell();
copy.setControllerId(source.getControllerId());
copy.setCopiedSpell(true);
game.getStack().push(copy);
copy.chooseNewTargets(game, source.getControllerId());
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString());
}
return true;
} }
return true;
} }
} }
return false; return false;
} }
public void setConspiredSpell(Spell conspiredSpell) {
this.conspiredSpell = conspiredSpell;
}
@Override @Override
public ConspireEffect copy() { public ConspireEffect copy() {
return new ConspireEffect(this); return new ConspireEffect(this);

View file

@ -106,6 +106,7 @@ public abstract class StackObjImpl implements StackObject {
public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
Player targetController = game.getPlayer(targetControllerId); Player targetController = game.getPlayer(targetControllerId);
if (targetController != null) { if (targetController != null) {
StringBuilder oldTargetDescription = new StringBuilder();
StringBuilder newTargetDescription = new StringBuilder(); StringBuilder newTargetDescription = new StringBuilder();
// Fused split spells or spells where "Splice on Arcane" was used can have more than one ability // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability
Abilities<Ability> objectAbilities = new AbilitiesImpl<>(); Abilities<Ability> objectAbilities = new AbilitiesImpl<>();
@ -118,6 +119,7 @@ public abstract class StackObjImpl implements StackObject {
// Some spells can have more than one mode // Some spells can have more than one mode
for (UUID modeId : ability.getModes().getSelectedModes()) { for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId); Mode mode = ability.getModes().get(modeId);
oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game));
for (Target target : mode.getTargets()) { for (Target target : mode.getTargets()) {
Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game);
// clear the old target and copy all targets from new target // clear the old target and copy all targets from new target
@ -131,7 +133,7 @@ public abstract class StackObjImpl implements StackObject {
} }
} }
if (newTargetDescription.length() > 0 && !game.isSimulation()) { if (!newTargetDescription.toString().equals(oldTargetDescription.toString()) && !game.isSimulation()) {
game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString());
} }
return true; return true;