diff --git a/Mage.Sets/src/mage/cards/a/Aethertow.java b/Mage.Sets/src/mage/cards/a/Aethertow.java index f126ce78317..f9af151c47c 100644 --- a/Mage.Sets/src/mage/cards/a/Aethertow.java +++ b/Mage.Sets/src/mage/cards/a/Aethertow.java @@ -31,7 +31,7 @@ public final class Aethertow extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private Aethertow(final Aethertow card) { diff --git a/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java b/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java index 15a587256d3..86c46bfb2bd 100644 --- a/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java +++ b/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java @@ -24,7 +24,7 @@ public final class BarkshellBlessing extends CardImpl { this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private BarkshellBlessing(final BarkshellBlessing card) { diff --git a/Mage.Sets/src/mage/cards/b/BurnTrail.java b/Mage.Sets/src/mage/cards/b/BurnTrail.java index f2620442ca4..9c070b730ae 100644 --- a/Mage.Sets/src/mage/cards/b/BurnTrail.java +++ b/Mage.Sets/src/mage/cards/b/BurnTrail.java @@ -23,7 +23,7 @@ public final class BurnTrail extends CardImpl { this.getSpellAbility().addTarget(new TargetAnyTarget()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private BurnTrail(final BurnTrail card) { diff --git a/Mage.Sets/src/mage/cards/d/DisturbingPlot.java b/Mage.Sets/src/mage/cards/d/DisturbingPlot.java index e9ced3a80ce..f8b24e66d33 100644 --- a/Mage.Sets/src/mage/cards/d/DisturbingPlot.java +++ b/Mage.Sets/src/mage/cards/d/DisturbingPlot.java @@ -24,7 +24,7 @@ public final class DisturbingPlot extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/d/DreamHalls.java b/Mage.Sets/src/mage/cards/d/DreamHalls.java index 19928b062e8..51b608af534 100644 --- a/Mage.Sets/src/mage/cards/d/DreamHalls.java +++ b/Mage.Sets/src/mage/cards/d/DreamHalls.java @@ -46,7 +46,7 @@ class DreamHallsEffect extends ContinuousEffectImpl { static { filter.add(new AnotherCardPredicate()); - filter.add(new SharesColorWithSourcePredicate()); + filter.add(SharesColorWithSourcePredicate.instance); } private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter), SourceIsSpellCondition.instance); diff --git a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java index 6243e09fe49..aed7f17c206 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java @@ -25,7 +25,7 @@ public final class GhastlyDiscovery extends CardImpl { this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.NONE)); } private GhastlyDiscovery(final GhastlyDiscovery card) { diff --git a/Mage.Sets/src/mage/cards/g/Giantbaiting.java b/Mage.Sets/src/mage/cards/g/Giantbaiting.java index 4f688b34145..112ce1272ee 100644 --- a/Mage.Sets/src/mage/cards/g/Giantbaiting.java +++ b/Mage.Sets/src/mage/cards/g/Giantbaiting.java @@ -26,7 +26,7 @@ public final class Giantbaiting extends CardImpl { this.getSpellAbility().addEffect(new GiantbaitingEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.NONE)); } diff --git a/Mage.Sets/src/mage/cards/g/GleefulSabotage.java b/Mage.Sets/src/mage/cards/g/GleefulSabotage.java index b8740eb911d..86b8463fb5b 100644 --- a/Mage.Sets/src/mage/cards/g/GleefulSabotage.java +++ b/Mage.Sets/src/mage/cards/g/GleefulSabotage.java @@ -25,7 +25,7 @@ public final class GleefulSabotage extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private GleefulSabotage(final GleefulSabotage card) { diff --git a/Mage.Sets/src/mage/cards/m/MemorySluice.java b/Mage.Sets/src/mage/cards/m/MemorySluice.java index a911e031f89..13ee4e0952f 100644 --- a/Mage.Sets/src/mage/cards/m/MemorySluice.java +++ b/Mage.Sets/src/mage/cards/m/MemorySluice.java @@ -23,7 +23,7 @@ public final class MemorySluice extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/m/MineExcavation.java b/Mage.Sets/src/mage/cards/m/MineExcavation.java index 815f33043ef..6c533d70803 100644 --- a/Mage.Sets/src/mage/cards/m/MineExcavation.java +++ b/Mage.Sets/src/mage/cards/m/MineExcavation.java @@ -32,7 +32,7 @@ public final class MineExcavation extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private MineExcavation(final MineExcavation card) { diff --git a/Mage.Sets/src/mage/cards/t/TraitorsRoar.java b/Mage.Sets/src/mage/cards/t/TraitorsRoar.java index 387646e7765..d789661e71b 100644 --- a/Mage.Sets/src/mage/cards/t/TraitorsRoar.java +++ b/Mage.Sets/src/mage/cards/t/TraitorsRoar.java @@ -35,7 +35,7 @@ public final class TraitorsRoar extends CardImpl { this.getSpellAbility().addEffect(new TraitorsRoarEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java index f8bc5b1c0cd..5cdfdad7600 100644 --- a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java +++ b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java @@ -59,17 +59,13 @@ 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.)"; - conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); } public WortGainConspireEffect(final WortGainConspireEffect effect) { super(effect); - this.conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); } @Override @@ -81,11 +77,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() && stackObject.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) stackObject; - if (filter.match(stackObject, game)) { - game.getState().addOtherAbility(spell.getCard(), conspireAbility); - } + 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)); } } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 43332eb8b5a..32de82ddf5b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -3,13 +3,11 @@ package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.*; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.CopySourceSpellEffect; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -18,14 +16,15 @@ 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 java.util.HashSet; +import java.util.Collection; import java.util.Iterator; -import java.util.Set; -import java.util.UUID; +import java.util.List; +import java.util.Objects; /* * 702.77. Conspire @@ -45,62 +44,54 @@ import java.util.UUID; public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Conspire"; - private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped creatures you control that share a color with it"); 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"); static { filter.add(TappedPredicate.UNTAPPED); - filter.add(new SharesColorWithSourcePredicate()); + filter.add(SharesColorWithSourcePredicate.instance); filter.add(CardType.CREATURE.getPredicate()); } public enum ConspireTargets { - NONE, - ONE, - MORE + NONE(""), + ONE(" and you may choose a new target for the copy"), + MORE(" and you may choose new targets for the copy"); + private final String message; + + ConspireTargets(String message) { + this.message = message; + } + + public String getReminder() { + return "as you cast this spell, you may tap two untapped creatures you control " + + "that share a color with it. When you do, copy it" + message; + } } - private final UUID conspireId; - private String reminderText; - private OptionalAdditionalCost conspireCost; + private final String reminderText; + private final OptionalAdditionalCost 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) { + public ConspireAbility(ConspireTargets conspireTargets) { super(Zone.STACK, null); - this.conspireId = conspireId; - 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 new targets for the copy."; - break; - } - - Cost cost = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); - cost.setText(""); - addConspireCostAndSetup(new OptionalAdditionalCostImpl(keywordText, " ", reminderText, cost)); - - addSubAbility(new ConspireTriggeredAbility(conspireId)); - } - - private void addConspireCostAndSetup(OptionalAdditionalCost newCost) { - this.conspireCost = newCost; + reminderText = conspireTargets.getReminder(); + this.conspireCost = new OptionalAdditionalCostImpl( + keywordText, " ", reminderText, + new TapTargetCost(new TargetControlledPermanent(2, filter)) + ); this.conspireCost.setCostType(VariableCostType.ADDITIONAL); + addSubAbility(new ConspireTriggeredAbility()); } public ConspireAbility(final ConspireAbility ability) { super(ability); - this.conspireId = ability.conspireId; this.conspireCost = ability.conspireCost.copy(); this.reminderText = ability.reminderText; } @@ -113,66 +104,39 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional @Override public void addCost(Cost cost) { if (conspireCost != null) { - ((Costs) conspireCost).add(cost); + ((Costs) conspireCost).add(cost); } } - public UUID getConspireId() { - return conspireId; - } - @Override public boolean isActivated() { throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!"); } - public boolean isActivated(Ability ability, Game game) { - Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); - if (activations != null) { - return activations.contains(getConspireId()); - } - return false; - } - @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { - if (ability instanceof SpellAbility) { - Player player = game.getPlayer(getControllerId()); - if (player != null) { - resetConspire(ability, game); - // AI supports conspire - if (conspireCost.canPay(ability, this, getControllerId(), game) - && player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { - activateConspire(ability, game); - for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) { - Cost cost = (Cost) it.next(); - if (cost instanceof ManaCostsImpl) { - ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); - } else { - ability.getCosts().add(cost.copy()); - } - } - } + if (!(ability instanceof SpellAbility)) { + return; + } + Player player = game.getPlayer(getControllerId()); + if (player == null) { + return; + } + // AI supports conspire + if (!conspireCost.canPay(ability, this, getControllerId(), game) + || !player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { + return; + } + for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCostsImpl) { + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } else { + ability.getCosts().add(cost.copy()); } } } - private void activateConspire(Ability ability, Game game) { - Set activations = (Set) 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 activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); - if (activations != null) { - activations.remove(getConspireId()); - } - } - @Override public String getRule() { StringBuilder sb = new StringBuilder(); @@ -185,35 +149,19 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional @Override public String getCastMessageSuffix() { - if (conspireCost != null) { - return conspireCost.getCastSuffixMessage(0); - } else { - return ""; - } - } - - public String getReminderText() { - if (conspireCost != null) { - return conspireCost.getReminderText(); - } else { - return ""; - } + return conspireCost != null ? conspireCost.getCastSuffixMessage(0) : ""; } } -class ConspireTriggeredAbility extends TriggeredAbilityImpl { +class ConspireTriggeredAbility extends CastSourceTriggeredAbility { - private final UUID conspireId; - - public ConspireTriggeredAbility(UUID conspireId) { - super(Zone.STACK, new ConspireEffect()); - this.conspireId = conspireId; + public ConspireTriggeredAbility() { + super(new CopySourceSpellEffect(), false); this.setRuleVisible(false); } private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { super(ability); - this.conspireId = ability.conspireId; } @Override @@ -221,34 +169,21 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return new ConspireTriggeredAbility(this); } - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { - Spell spell = game.getStack().getSpell(event.getSourceId()); - for (Ability ability : spell.getAbilities(game)) { - if (ability instanceof ConspireAbility - && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) { - if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) { - for (Effect effect : this.getEffects()) { - if (effect instanceof ConspireEffect) { - ((ConspireEffect) effect).setConspiredSpell(spell); - } - } - return true; - } - } - } + if (!super.checkTrigger(event, game)) { + return false; } - return false; - } - - public UUID getConspireId() { - return conspireId; + Spell spell = game.getStack().getSpell(event.getSourceId()); + return spell != null && spell + .getSpellAbility() + .getEffects() + .stream() + .map(effect -> effect.getValue("tappedPermanents")) + .filter(Objects::nonNull) + .map(x -> (List) x) + .flatMap(Collection::stream) + .count() >= 2; } @Override @@ -256,39 +191,3 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return "When you pay the conspire costs, copy it and you may choose a new target for the copy."; } } - -class ConspireEffect extends OneShotEffect { - - private Spell conspiredSpell; - - public ConspireEffect() { - super(Outcome.Copy); - } - - public ConspireEffect(final ConspireEffect effect) { - super(effect); - this.conspiredSpell = effect.conspiredSpell; - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && conspiredSpell != null) { - Card card = game.getCard(conspiredSpell.getSourceId()); - if (card != null) { - conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } - } - return false; - } - - public void setConspiredSpell(Spell conspiredSpell) { - this.conspiredSpell = conspiredSpell; - } - - @Override - public ConspireEffect copy() { - return new ConspireEffect(this); - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java index 3beae46614e..bd76f122ecc 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java @@ -6,11 +6,11 @@ import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; /** - * * @author LevelX2 */ -public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredicate { +public enum SharesColorWithSourcePredicate implements ObjectSourcePlayerPredicate { + instance; @Override public boolean apply(ObjectSourcePlayer input, Game game) { @@ -26,4 +26,4 @@ public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredica public String toString() { return "shares a color"; } -} \ No newline at end of file +}