From 6903dad861d22dff83e7629689d9bcefa3dcaf60 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 26 Aug 2020 18:48:59 -0400 Subject: [PATCH] Implemented Jace, Mirror Mage --- .../src/mage/cards/j/JaceMirrorMage.java | 134 ++++++++++++++++++ Mage.Sets/src/mage/sets/ZendikarRising.java | 1 + Mage/src/main/java/mage/MageObject.java | 3 + Mage/src/main/java/mage/MageObjectImpl.java | 12 +- ...alkerEntersWithLoyaltyCountersAbility.java | 13 +- .../common/CreateTokenCopyTargetEffect.java | 10 ++ Utils/keywords.txt | 1 + 7 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/j/JaceMirrorMage.java diff --git a/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java new file mode 100644 index 0000000000..4e663ea29b --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java @@ -0,0 +1,134 @@ +package mage.cards.j; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * @author TheElk801 + */ +public final class JaceMirrorMage extends CardImpl { + + public JaceMirrorMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.JACE); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // Kicker {2} + this.addAbility(new KickerAbility(new ManaCostsImpl<>("{2}"))); + + // When Jace, Mirror Mage enters the battlefield, if Jace was kicked, create a token that's a copy of Jace, Mirror Mage except it's not legendary and its starting loyalty is 1. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new JaceMirrorMageCopyEffect()), + KickedCondition.instance, "When {this} enters the battlefield, if {this} was kicked, " + + "create a token that's a copy of {this} except it's not legendary and its starting loyalty is 1." + )); + + // +1: Scry 2. + this.addAbility(new LoyaltyAbility(new ScryEffect(2), 1)); + + // 0: Draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from Jace, Mirror Mage. + this.addAbility(new LoyaltyAbility(new JaceMirrorMageDrawEffect(), 0)); + } + + private JaceMirrorMage(final JaceMirrorMage card) { + super(card); + } + + @Override + public JaceMirrorMage copy() { + return new JaceMirrorMage(this); + } +} + +class JaceMirrorMageCopyEffect extends OneShotEffect { + + JaceMirrorMageCopyEffect() { + super(Outcome.Benefit); + } + + private JaceMirrorMageCopyEffect(final JaceMirrorMageCopyEffect effect) { + super(effect); + } + + @Override + public JaceMirrorMageCopyEffect copy() { + return new JaceMirrorMageCopyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, false, 1); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); + effect.setIsntLegendary(true); + effect.setStartingLoyalty(1); + return effect.apply(game, source); + } +} + +class JaceMirrorMageDrawEffect extends OneShotEffect { + + JaceMirrorMageDrawEffect() { + super(Outcome.Benefit); + staticText = "draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from {this}"; + } + + private JaceMirrorMageDrawEffect(final JaceMirrorMageDrawEffect effect) { + super(effect); + } + + @Override + public JaceMirrorMageDrawEffect copy() { + return new JaceMirrorMageDrawEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // TODO: Make this and similar effects work with draw replacement correctly + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + player.drawCards(1, source.getSourceId(), game); + player.revealCards(source, new CardsImpl(card), game); + if (card == null || card.getConvertedManaCost() == 0) { + return true; + } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return true; + } + permanent.removeCounters(CounterType.LOYALTY.createInstance(card.getConvertedManaCost()), game); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/ZendikarRising.java b/Mage.Sets/src/mage/sets/ZendikarRising.java index 411845a1e7..bca17a852c 100644 --- a/Mage.Sets/src/mage/sets/ZendikarRising.java +++ b/Mage.Sets/src/mage/sets/ZendikarRising.java @@ -26,5 +26,6 @@ public final class ZendikarRising extends ExpansionSet { this.ratioBoosterMythic = 8; this.maxCardNumberInBooster = 280; + cards.add(new SetCardInfo("Jace, Mirror Mage", 63, Rarity.MYTHIC, mage.cards.j.JaceMirrorMage.class)); } } diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 53bb7d9cef..fd55bb3893 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Set; + import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; @@ -65,6 +66,8 @@ public interface MageObject extends MageItem, Serializable { int getStartingLoyalty(); + void setStartingLoyalty(int startingLoyalty); + void adjustCosts(Ability ability, Game game); void adjustTargets(Ability ability, Game game); diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index f8047cb8df..aa3792e486 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -1,6 +1,7 @@ package mage; import java.util.*; + import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -160,6 +161,15 @@ public abstract class MageObjectImpl implements MageObject { return 0; } + @Override + public void setStartingLoyalty(int startingLoyalty) { + for (Ability ab : getAbilities()) { + if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) { + ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).setStartingLoyalty(startingLoyalty); + } + } + } + @Override public ObjectColor getColor(Game game) { return color; @@ -307,7 +317,7 @@ public abstract class MageObjectImpl implements MageObject { */ @Override public void removePTCDA() { - for (Iterator iter = this.getAbilities().iterator(); iter.hasNext();) { + for (Iterator iter = this.getAbilities().iterator(); iter.hasNext(); ) { Ability ability = iter.next(); for (Effect effect : ability.getEffects()) { if (effect instanceof ContinuousEffect && ((ContinuousEffect) effect).getSublayer() == SubLayer.CharacteristicDefining_7a) { diff --git a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java b/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java index 58cecdbb69..e406eda510 100644 --- a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java @@ -9,13 +9,12 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.counters.CounterType; /** - * * @author LevelX2 */ public class PlaneswalkerEntersWithLoyaltyCountersAbility extends EntersBattlefieldAbility { - private final int startingLoyalty; - + private int startingLoyalty; + public PlaneswalkerEntersWithLoyaltyCountersAbility(int loyalty) { super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyalty))); startingLoyalty = loyalty; @@ -26,11 +25,17 @@ public class PlaneswalkerEntersWithLoyaltyCountersAbility extends EntersBattlefi super(ability); startingLoyalty = ability.startingLoyalty; } - + public int getStartingLoyalty() { return startingLoyalty; } + public void setStartingLoyalty(int startingLoyalty) { + this.startingLoyalty = startingLoyalty; + this.getEffects().clear(); + this.addEffect(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(startingLoyalty))); + } + @Override public PlaneswalkerEntersWithLoyaltyCountersAbility copy() { return new PlaneswalkerEntersWithLoyaltyCountersAbility(this); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 7ffcd7f0ee..3b01e3e4be 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; + import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -46,6 +47,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { private ObjectColor color; private boolean useLKI = false; private boolean isntLegendary = false; + private int startingLoyalty = -1; private final List additionalAbilities = new ArrayList(); public CreateTokenCopyTargetEffect(boolean useLKI) { @@ -176,6 +178,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { if (isntLegendary) { token.getSuperType().remove(SuperType.LEGENDARY); } + + if (startingLoyalty!=-1){ + token.setStartingLoyalty(startingLoyalty); + } if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { token.addCardType(additionalCardType); } @@ -288,6 +294,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this.hasHaste = hasHaste; } + public void setStartingLoyalty(int startingLoyalty) { + this.startingLoyalty = startingLoyalty; + } + public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { for (Permanent tokenPermanent : addedTokenPermanents) { ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); diff --git a/Utils/keywords.txt b/Utils/keywords.txt index 2d66132a34..4fc1cd06b8 100644 --- a/Utils/keywords.txt +++ b/Utils/keywords.txt @@ -53,6 +53,7 @@ Ingest|new| Islandcycling|cost| Islandwalk|new| Jump-start|card| +Kicker|cost| Level up|cost| Lifelink|instance| Living weapon|new|