diff --git a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
index 5cdfdad760..89a6007974 100644
--- a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
+++ b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
@@ -35,10 +35,12 @@ public final class WortTheRaidmother extends CardImpl {
this.toughness = new MageInt(3);
// When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens.
- this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinWarriorToken(), 2), false));
+ this.addAbility(new EntersBattlefieldTriggeredAbility(
+ new CreateTokenEffect(new GoblinWarriorToken(), 2)
+ ));
// Each red or green instant or sorcery spell you cast has conspire.
- this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WortGainConspireEffect()));
+ this.addAbility(new SimpleStaticAbility(new WortGainConspireEffect()));
}
private WortTheRaidmother(final WortTheRaidmother card) {
@@ -59,13 +61,17 @@ class WortGainConspireEffect extends ContinuousEffectImpl {
filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN)));
}
+ private final ConspireAbility conspireAbility;
+
public WortGainConspireEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
staticText = "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.)";
+ this.conspireAbility = new ConspireAbility(ConspireAbility.ConspireTargets.MORE);
}
public WortGainConspireEffect(final WortGainConspireEffect effect) {
super(effect);
+ this.conspireAbility = effect.conspireAbility;
}
@Override
@@ -77,13 +83,13 @@ class WortGainConspireEffect extends ContinuousEffectImpl {
public boolean apply(Game game, Ability source) {
for (StackObject stackObject : game.getStack()) {
// only spells cast, so no copies of spells
- if ((!(stackObject instanceof Spell)) || stackObject.isCopy()
+ if (!(stackObject instanceof Spell) || stackObject.isCopy()
|| !stackObject.isControlledBy(source.getControllerId())) {
continue;
}
Spell spell = (Spell) stackObject;
if (filter.match(stackObject, game)) {
- game.getState().addOtherAbility(spell.getCard(), new ConspireAbility(ConspireAbility.ConspireTargets.MORE));
+ game.getState().addOtherAbility(spell.getCard(), conspireAbility.setAddedById(source.getSourceId()));
}
}
return true;
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java
index e9d95cdb6c..617b7a1864 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java
@@ -48,7 +48,7 @@ public class ConspireTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
- assertLife(playerB, 14);
+ assertLife(playerB, 20 - 3 - 3);
assertGraveyardCount(playerA, "Burn Trail", 1);
assertTapped("Goblin Roughrider", true);
assertTapped("Raging Goblin", true);
@@ -68,7 +68,7 @@ public class ConspireTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
- assertLife(playerB, 17);
+ assertLife(playerB, 20 - 3);
assertGraveyardCount(playerA, "Burn Trail", 1);
assertTapped("Goblin Roughrider", false);
assertTapped("Raging Goblin", false);
@@ -101,12 +101,35 @@ public class ConspireTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Wort, the Raidmother", 1);
assertGraveyardCount(playerA, "Lightning Bolt", 1);
- assertLife(playerB, 14);
+ assertLife(playerB, 20 - 3 - 3);
}
@Test
- public void testWortTheRaidmotherWithConspireSpell() {
+ public void testWortTheRaidmotherWithConspireSpellOnce() {
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10);
+ // 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, true); // use Conspire from Burn Trail itself
+ setChoice(playerA, false); // don't use Conspire gained from Wort, the Raidmother
+
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertPermanentCount(playerA, "Wort, the Raidmother", 1);
+ assertLife(playerB, 20 - 3 - 3);
+ assertLife(playerA, 20);
+ assertGraveyardCount(playerA, "Burn Trail", 1);
+ }
+
+ @Test
+ public void testWortTheRaidmotherWithConspireSpellTwice() {
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.
@@ -124,12 +147,66 @@ public class ConspireTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Wort, the Raidmother", 1);
- assertLife(playerB, 11);
+ assertLife(playerB, 20 - 3 - 3 - 3);
assertLife(playerA, 20);
assertGraveyardCount(playerA, "Burn Trail", 1);
}
+ @Test
+ public void testWortTheRaidmotherWithSakashimaOnce() {
+ addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 11);
+ // 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, "Sakashima the Impostor");
+ addCard(Zone.HAND, playerA, "Lightning Bolt");
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G}
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sakashima the Impostor"); // {2}{U}{U}
+ setChoice(playerA, "Wort, the Raidmother");
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+ setChoice(playerA, true); // use Conspire gained from Wort, the Raidmother
+ setChoice(playerA, false); // don't use Conspire gained from Sakashima the Imposter
+
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertPermanentCount(playerA, "Wort, the Raidmother", 1);
+ assertPermanentCount(playerA, "Sakashima the Impostor", 1);
+ assertLife(playerB, 20 - 3 - 3);
+ assertLife(playerA, 20);
+ assertGraveyardCount(playerA, "Lightning Bolt", 1);
+ }
+
+ @Test
+ public void testWortTheRaidmotherWithSakashimaTwice() {
+ addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 11);
+ // 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, "Sakashima the Impostor");
+ addCard(Zone.HAND, playerA, "Lightning Bolt");
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G}
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sakashima the Impostor"); // {2}{U}{U}
+ setChoice(playerA, "Wort, the Raidmother");
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+ setChoice(playerA, true); // use Conspire gained from Wort, the Raidmother
+ setChoice(playerA, true); // use Conspire gained from Sakashima the Imposter
+
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+ assertPermanentCount(playerA, "Wort, the Raidmother", 1);
+ assertPermanentCount(playerA, "Sakashima the Impostor", 1);
+ assertLife(playerB, 20 - 3 - 3 - 3);
+ assertLife(playerA, 20);
+ assertGraveyardCount(playerA, "Lightning Bolt", 1);
+ }
+
@Test
public void testConspire_User() {
// Burn Trail deals 3 damage to any target.
@@ -151,7 +228,7 @@ public class ConspireTest extends CardTestPlayerBase {
assertAllCommandsUsed();
assertGraveyardCount(playerA, "Burn Trail", 1);
- assertLife(playerB, 20 - 3 * 2);
+ assertLife(playerB, 20 - 3 - 3);
assertTapped("Goblin Assailant", true);
}
@@ -176,7 +253,7 @@ public class ConspireTest extends CardTestPlayerBase {
assertAllCommandsUsed();
assertGraveyardCount(playerA, "Burn Trail", 1);
- assertLife(playerB, 20 - 3 * 2);
+ assertLife(playerB, 20 - 3 - 3);
assertTapped("Goblin Assailant", true);
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java
index 32de82ddf5..7532725b29 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java
@@ -16,15 +16,14 @@ import mage.filter.predicate.mageobject.SharesColorWithSourcePredicate;
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.Player;
import mage.target.common.TargetControlledPermanent;
+import mage.util.CardUtil;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.Objects;
+import java.util.UUID;
/*
* 702.77. Conspire
@@ -44,7 +43,6 @@ import java.util.Objects;
public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
private static final String keywordText = "Conspire";
- protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation";
private static final FilterControlledPermanent filter
= new FilterControlledPermanent("untapped creatures you control that share a color with it");
@@ -70,6 +68,8 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
}
}
+ private final UUID conspireId;
+ private UUID addedById = null;
private final String reminderText;
private final OptionalAdditionalCost conspireCost;
@@ -81,17 +81,20 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
*/
public ConspireAbility(ConspireTargets conspireTargets) {
super(Zone.STACK, null);
+ this.conspireId = UUID.randomUUID();
reminderText = conspireTargets.getReminder();
this.conspireCost = new OptionalAdditionalCostImpl(
keywordText, " ", reminderText,
new TapTargetCost(new TargetControlledPermanent(2, filter))
);
this.conspireCost.setCostType(VariableCostType.ADDITIONAL);
- addSubAbility(new ConspireTriggeredAbility());
+ this.addSubAbility(new ConspireTriggeredAbility(conspireId));
}
public ConspireAbility(final ConspireAbility ability) {
super(ability);
+ this.conspireId = ability.conspireId;
+ this.addedById = ability.addedById;
this.conspireCost = ability.conspireCost.copy();
this.reminderText = ability.reminderText;
}
@@ -124,9 +127,11 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
}
// AI supports conspire
if (!conspireCost.canPay(ability, this, getControllerId(), game)
- || !player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) {
+ || !player.chooseUse(Outcome.Benefit, "Pay "
+ + conspireCost.getText(false) + " ?", ability, game)) {
return;
}
+ ability.getEffects().setValue("ConspireActivation" + conspireId + addedById, true);
for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) {
Cost cost = (Cost) it.next();
if (cost instanceof ManaCostsImpl) {
@@ -151,17 +156,32 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
public String getCastMessageSuffix() {
return conspireCost != null ? conspireCost.getCastSuffixMessage(0) : "";
}
+
+ public ConspireAbility setAddedById(UUID addedById) {
+ this.addedById = addedById;
+ CardUtil.castStream(
+ this.subAbilities.stream(),
+ ConspireTriggeredAbility.class
+ ).forEach(ability -> ability.setAddedById(addedById));
+ return this;
+ }
}
class ConspireTriggeredAbility extends CastSourceTriggeredAbility {
- public ConspireTriggeredAbility() {
+ private final UUID conspireId;
+ private UUID addedById = null;
+
+ public ConspireTriggeredAbility(UUID conspireId) {
super(new CopySourceSpellEffect(), false);
this.setRuleVisible(false);
+ this.conspireId = conspireId;
}
private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) {
super(ability);
+ this.conspireId = ability.conspireId;
+ this.addedById = ability.addedById;
}
@Override
@@ -175,19 +195,21 @@ class ConspireTriggeredAbility extends CastSourceTriggeredAbility {
return false;
}
Spell spell = game.getStack().getSpell(event.getSourceId());
- return spell != null && spell
+ return spell != null
+ && spell
.getSpellAbility()
.getEffects()
.stream()
- .map(effect -> effect.getValue("tappedPermanents"))
- .filter(Objects::nonNull)
- .map(x -> (List) x)
- .flatMap(Collection::stream)
- .count() >= 2;
+ .map(effect -> effect.getValue("ConspireActivation" + conspireId + addedById))
+ .anyMatch(Objects::nonNull);
}
@Override
public String getRule() {
return "When you pay the conspire costs, copy it and you may choose a new target for the copy.";
}
+
+ public void setAddedById(UUID addedById) {
+ this.addedById = addedById;
+ }
}