diff --git a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java new file mode 100644 index 0000000000..b6e7012192 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java @@ -0,0 +1,76 @@ +package mage.cards.c; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.functions.ApplyToPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrystallineResonance extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("another permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + public CrystallineResonance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // Whenever you cycle a card, you may have Crystalline Resonance become a copy of another target permanent until your next turn, except it has this ability. + this.addAbility(this.createAbility()); + } + + private CrystallineResonance(final CrystallineResonance card) { + super(card); + } + + @Override + public CrystallineResonance copy() { + return new CrystallineResonance(this); + } + + static Ability createAbility() { + Ability ability = new CycleControllerTriggeredAbility( + new CopyPermanentEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, + new CrystallineResonanceApplier(), true + ).setDuration(Duration.UntilYourNextTurn).setText( + "have {this} become a copy of another target permanent until your next turn, " + + "except it has this ability" + ), true + ); + ability.addTarget(new TargetPermanent(filter)); + return ability; + } +} + +class CrystallineResonanceApplier extends ApplyToPermanent { + + @Override + public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { + permanent.getAbilities().add(CrystallineResonance.createAbility()); + return true; + } + + @Override + public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { + mageObject.getAbilities().add(CrystallineResonance.createAbility()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrannithStinger.java b/Mage.Sets/src/mage/cards/d/DrannithStinger.java index 7c029631a5..03c72c6350 100644 --- a/Mage.Sets/src/mage/cards/d/DrannithStinger.java +++ b/Mage.Sets/src/mage/cards/d/DrannithStinger.java @@ -28,7 +28,7 @@ public final class DrannithStinger extends CardImpl { // Whenever you cycle another card, Drannith Stinger deals 1 damage to each opponent. this.addAbility(new CycleControllerTriggeredAbility( - new DamagePlayersEffect(1, TargetController.OPPONENT) + new DamagePlayersEffect(1, TargetController.OPPONENT), false, true )); // Cycling {1} diff --git a/Mage.Sets/src/mage/cards/f/FlourishingFox.java b/Mage.Sets/src/mage/cards/f/FlourishingFox.java index 744548915e..11b3a45add 100644 --- a/Mage.Sets/src/mage/cards/f/FlourishingFox.java +++ b/Mage.Sets/src/mage/cards/f/FlourishingFox.java @@ -27,7 +27,7 @@ public final class FlourishingFox extends CardImpl { // Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox. this.addAbility(new CycleControllerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true )); // Cycling {1} diff --git a/Mage.Sets/src/mage/sets/Commander2020Edition.java b/Mage.Sets/src/mage/sets/Commander2020Edition.java index acfec018ac..f906fc98bd 100644 --- a/Mage.Sets/src/mage/sets/Commander2020Edition.java +++ b/Mage.Sets/src/mage/sets/Commander2020Edition.java @@ -71,6 +71,7 @@ public final class Commander2020Edition extends ExpansionSet { cards.add(new SetCardInfo("Commander's Sphere", 240, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); cards.add(new SetCardInfo("Crop Rotation", 169, Rarity.COMMON, mage.cards.c.CropRotation.class)); cards.add(new SetCardInfo("Cryptic Trilobite", 21, Rarity.RARE, mage.cards.c.CrypticTrilobite.class)); + cards.add(new SetCardInfo("Crystalline Resonance", 31, Rarity.RARE, mage.cards.c.CrystallineResonance.class)); cards.add(new SetCardInfo("Curious Herd", 59, Rarity.RARE, mage.cards.c.CuriousHerd.class)); cards.add(new SetCardInfo("Daring Fiendbonder", 41, Rarity.RARE, mage.cards.d.DaringFiendbonder.class)); cards.add(new SetCardInfo("Deadly Tempest", 131, Rarity.RARE, mage.cards.d.DeadlyTempest.class)); diff --git a/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java index 2f1bd592a0..5911d5c445 100644 --- a/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java @@ -14,16 +14,24 @@ import mage.game.stack.StackObject; */ public class CycleControllerTriggeredAbility extends TriggeredAbilityImpl { + private final boolean excludeSource; + public CycleControllerTriggeredAbility(Effect effect) { this(effect, false); } public CycleControllerTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, false); + } + + public CycleControllerTriggeredAbility(Effect effect, boolean optional, boolean excludeSource) { super(Zone.BATTLEFIELD, effect, optional); + this.excludeSource = excludeSource; } private CycleControllerTriggeredAbility(final CycleControllerTriggeredAbility ability) { super(ability); + this.excludeSource = ability.excludeSource; } @Override @@ -35,7 +43,7 @@ public class CycleControllerTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (game.getState().getStack().isEmpty() || !event.getPlayerId().equals(this.getControllerId()) - || event.getSourceId().equals(this.getSourceId())) { + || (event.getSourceId().equals(this.getSourceId()) && excludeSource)) { return false; } StackObject item = game.getState().getStack().getFirst(); @@ -45,7 +53,7 @@ public class CycleControllerTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever you cycle another card, " + super.getRule(); + return "Whenever you cycle " + (excludeSource ? "another" : "a") + " card, " + super.getRule(); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java index 3485c7c4f9..23757957d9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java @@ -6,11 +6,11 @@ import mage.abilities.SpellAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.EnchantAbility; +import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -26,13 +26,14 @@ import java.util.UUID; */ public class CopyPermanentEffect extends OneShotEffect { - private FilterPermanent filter; - private ApplyToPermanent applier; + private final FilterPermanent filter; + private final ApplyToPermanent applier; + private final boolean useTargetOfAbility; private Permanent bluePrintPermanent; - private boolean useTargetOfAbility; + private Duration duration = Duration.Custom; public CopyPermanentEffect() { - this(new FilterCreaturePermanent()); + this(StaticFilters.FILTER_PERMANENT_CREATURE); } public CopyPermanentEffect(ApplyToPermanent applier) { @@ -72,88 +73,95 @@ public class CopyPermanentEffect extends OneShotEffect { if (sourcePermanent == null) { sourcePermanent = game.getObject(source.getSourceId()); } - if (controller != null && sourcePermanent != null) { - Permanent copyFromPermanent = null; - if (useTargetOfAbility) { - copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - } else { - Target target = new TargetPermanent(filter); - target.setNotTarget(true); - if (target.canChoose(source.getSourceId(), controller.getId(), game)) { - controller.choose(Outcome.Copy, target, source.getSourceId(), game); - copyFromPermanent = game.getPermanent(target.getFirstTarget()); - } - } - if (copyFromPermanent != null) { - bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, applier); - if (bluePrintPermanent == null) { - return false; - } - - // if object is a copy of an aura, it needs to attach again for new target - if (bluePrintPermanent.hasSubtype(SubType.AURA, game)) { - //copied from mage.cards.c.CopyEnchantment.java - - // permanent can be attached (Estrid's Mask) or enchant (Utopia Sprawl) - // TODO: fix Animate Dead -- it's can't be copied (can't retarget) - Outcome auraOutcome = Outcome.BoostCreature; - Target auraTarget = null; - - // attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it) - for (Ability ability : bluePrintPermanent.getAbilities()) { - if (ability instanceof SpellAbility) { - auraOutcome = ability.getEffects().getOutcome(ability); - for (Effect effect : ability.getEffects()) { - if (effect instanceof AttachEffect) { - if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) { - auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0); - } - } - } - } - } - - // enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it) - if (auraTarget == null) { - for (Ability ability : bluePrintPermanent.getAbilities()) { - if (ability instanceof EnchantAbility) { - auraOutcome = ability.getEffects().getOutcome(ability); - if (ability.getTargets().size() > 0) { // Animate Dead don't have targets - auraTarget = ability.getTargets().get(0); - } - } - } - } - - /* if this is a copy of a copy, the copy's target has been - * copied and needs to be cleared - */ - if (auraTarget != null) { - // clear selected target - if (auraTarget.getFirstTarget() != null) { - auraTarget.remove(auraTarget.getFirstTarget()); - } - - // select new target - auraTarget.setNotTarget(true); - if (controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) { - UUID targetId = auraTarget.getFirstTarget(); - Permanent targetPermanent = game.getPermanent(targetId); - Player targetPlayer = game.getPlayer(targetId); - if (targetPermanent != null) { - targetPermanent.addAttachment(sourcePermanent.getId(), game); - } else if (targetPlayer != null) { - targetPlayer.addAttachment(sourcePermanent.getId(), game); - } else { - return false; - } - } - } - } + if (controller == null || sourcePermanent == null) { + return false; + } + Permanent copyFromPermanent = null; + if (useTargetOfAbility) { + copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + } else { + Target target = new TargetPermanent(filter); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), controller.getId(), game)) { + controller.choose(Outcome.Copy, target, source.getSourceId(), game); + copyFromPermanent = game.getPermanent(target.getFirstTarget()); } + } + if (copyFromPermanent == null) { return true; } - return false; + bluePrintPermanent = game.copyPermanent(duration, copyFromPermanent, sourcePermanent.getId(), source, applier); + if (bluePrintPermanent == null) { + return false; + } + + // if object is a copy of an aura, it needs to attach again for new target + if (!bluePrintPermanent.hasSubtype(SubType.AURA, game)) { + return true; + } + //copied from mage.cards.c.CopyEnchantment.java + + // permanent can be attached (Estrid's Mask) or enchant (Utopia Sprawl) + // TODO: fix Animate Dead -- it's can't be copied (can't retarget) + Outcome auraOutcome = Outcome.BoostCreature; + Target auraTarget = null; + + // attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it) + for (Ability ability : bluePrintPermanent.getAbilities()) { + if (!(ability instanceof SpellAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + for (Effect effect : ability.getEffects()) { + if (!(effect instanceof AttachEffect)) { + continue; + } + if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) { + auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0); + } + } + } + + // enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it) + if (auraTarget == null) { + for (Ability ability : bluePrintPermanent.getAbilities()) { + if (!(ability instanceof EnchantAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + if (ability.getTargets().size() > 0) { // Animate Dead don't have targets + auraTarget = ability.getTargets().get(0); + } + } + } + + /* if this is a copy of a copy, the copy's target has been + * copied and needs to be cleared + */ + if (auraTarget == null) { + return true; + } + // clear selected target + if (auraTarget.getFirstTarget() != null) { + auraTarget.remove(auraTarget.getFirstTarget()); + } + + // select new target + auraTarget.setNotTarget(true); + if (!controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) { + return true; + } + UUID targetId = auraTarget.getFirstTarget(); + Permanent targetPermanent = game.getPermanent(targetId); + Player targetPlayer = game.getPlayer(targetId); + if (targetPermanent != null) { + targetPermanent.addAttachment(sourcePermanent.getId(), game); + } else if (targetPlayer != null) { + targetPlayer.addAttachment(sourcePermanent.getId(), game); + } else { + return false; + } + return true; } public Permanent getBluePrintPermanent() { @@ -164,4 +172,9 @@ public class CopyPermanentEffect extends OneShotEffect { public CopyPermanentEffect copy() { return new CopyPermanentEffect(this); } + + public CopyPermanentEffect setDuration(Duration duration) { + this.duration = duration; + return this; + } }