From 01261575a80dc4e00dfbabf7fee13de6785e13ea Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 10 Jan 2021 15:24:42 -0500 Subject: [PATCH] [KHM] Niko Aris --- Mage.Sets/src/mage/cards/n/NikoAris.java | 180 ++++++++++++++++++ Mage.Sets/src/mage/sets/Kaldheim.java | 1 + .../src/main/java/mage/constants/SubType.java | 2 + .../mage/game/permanent/token/ShardToken.java | 39 ++++ 4 files changed, 222 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/n/NikoAris.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/ShardToken.java diff --git a/Mage.Sets/src/mage/cards/n/NikoAris.java b/Mage.Sets/src/mage/cards/n/NikoAris.java new file mode 100644 index 0000000000..d6240a7374 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NikoAris.java @@ -0,0 +1,180 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.CardsDrawnThisTurnDynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.ShardToken; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.Watcher; +import mage.watchers.common.CardsDrawnThisTurnWatcher; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NikoAris extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("tapped creature"); + + static { + filter.add(TappedPredicate.instance); + } + + private static final DynamicValue xValue = new MultipliedValue(CardsDrawnThisTurnDynamicValue.instance, 2); + + public NikoAris(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{X}{W}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIKO); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + + // When Niko Aris enters the battlefield, create X Shard tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new CreateTokenEffect(new ShardToken(), NikoArisValue.instance) + ), new NikoArisWatcher()); + + // +1: Up to one target creature you control can't be blocked this turn. Whenever that creature deals damage this turn, return it to its owner's hand. + Ability ability = new LoyaltyAbility(new CantBeBlockedTargetEffect(Duration.EndOfTurn), 1); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new NikoArisDamageTriggeredAbility())); + ability.addTarget(new TargetControlledCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −1: Niko Aris deals 2 damage to target tapped creature for each card you've drawn this turn. + ability = new LoyaltyAbility(new DamageTargetEffect(xValue).setText( + "{this} deals 2 damage to target tapped creature for each card you've drawn this turn" + ), -1); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability, new CardsDrawnThisTurnWatcher()); + + // −1: Create a Shard token. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new ShardToken()), -1)); + } + + private NikoAris(final NikoAris card) { + super(card); + } + + @Override + public NikoAris copy() { + return new NikoAris(this); + } +} + +enum NikoArisValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + NikoArisWatcher watcher = game.getState().getWatcher(NikoArisWatcher.class, sourceAbility.getSourceId()); + if (watcher == null) { + return 0; + } + if (game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue") == null) { + return 0; + } + return (Integer) game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue"); + } + + @Override + public DynamicValue copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return ""; + } +} + +class NikoArisWatcher extends Watcher { + + NikoArisWatcher() { + super(WatcherScope.CARD); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpellOrLKIStack(event.getTargetId()); + if (spell == null || spell.getSourceId() != super.getSourceId()) { + return; + } + game.getState().setValue( + spell.getSourceId().toString() + "xValue", + spell.getSpellAbility().getManaCostsToPay().getX() + ); + } +} + +class NikoArisDamageTriggeredAbility extends DelayedTriggeredAbility { + + NikoArisDamageTriggeredAbility() { + super(new ReturnToHandTargetEffect(), Duration.EndOfTurn, false); + } + + private NikoArisDamageTriggeredAbility(final NikoArisDamageTriggeredAbility ability) { + super(ability); + } + + @Override + public NikoArisDamageTriggeredAbility copy() { + return new NikoArisDamageTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGED_PLAYER: + case DAMAGED_CREATURE: + case DAMAGED_PLANESWALKER: + return true; + } + return false; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return this.getEffects() + .stream() + .filter(Objects::nonNull) + .map(Effect::getTargetPointer) + .map(targetPointer -> targetPointer.getFirst(game, this)) + .anyMatch(event.getSourceId()::equals); + } + + @Override + public String getRule() { + return "Whenever that creature deals damage this turn, return it to its owner's hand."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Kaldheim.java b/Mage.Sets/src/mage/sets/Kaldheim.java index be7bd0de9b..3e180e84b8 100644 --- a/Mage.Sets/src/mage/sets/Kaldheim.java +++ b/Mage.Sets/src/mage/sets/Kaldheim.java @@ -91,6 +91,7 @@ public final class Kaldheim extends ExpansionSet { cards.add(new SetCardInfo("Koma's Faithful", 102, Rarity.COMMON, mage.cards.k.KomasFaithful.class)); cards.add(new SetCardInfo("Magda, Brazen Outlaw", 142, Rarity.RARE, mage.cards.m.MagdaBrazenOutlaw.class)); cards.add(new SetCardInfo("Masked Vandal", 184, Rarity.COMMON, mage.cards.m.MaskedVandal.class)); + cards.add(new SetCardInfo("Niko Aris", 225, Rarity.MYTHIC, mage.cards.n.NikoAris.class)); cards.add(new SetCardInfo("Pyre of Heroes", 241, Rarity.RARE, mage.cards.p.PyreOfHeroes.class)); cards.add(new SetCardInfo("Rampage of the Valkyries", 393, Rarity.UNCOMMON, mage.cards.r.RampageOfTheValkyries.class)); cards.add(new SetCardInfo("Ravenform", 72, Rarity.COMMON, mage.cards.r.Ravenform.class)); diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 4682c8bda6..4f355e2a23 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -33,6 +33,7 @@ public enum SubType { CARTOUCHE("Cartouche", SubTypeSet.EnchantmentType), CURSE("Curse", SubTypeSet.EnchantmentType), SAGA("Saga", SubTypeSet.EnchantmentType), + SHARD("Shard", SubTypeSet.EnchantmentType), SHRINE("Shrine", SubTypeSet.EnchantmentType), // 205.3g: Artifacts have their own unique set of subtypes; these subtypes are called artifact types. CLUE("Clue", SubTypeSet.ArtifactType), @@ -415,6 +416,7 @@ public enum SubType { LUKKA("Lukka", SubTypeSet.PlaneswalkerType), NAHIRI("Nahiri", SubTypeSet.PlaneswalkerType), NARSET("Narset", SubTypeSet.PlaneswalkerType), + NIKO("Niko", SubTypeSet.PlaneswalkerType), NISSA("Nissa", SubTypeSet.PlaneswalkerType), NIXILIS("Nixilis", SubTypeSet.PlaneswalkerType), OBI_WAN("Obi-Wan", SubTypeSet.PlaneswalkerType, true), // Star Wars diff --git a/Mage/src/main/java/mage/game/permanent/token/ShardToken.java b/Mage/src/main/java/mage/game/permanent/token/ShardToken.java new file mode 100644 index 0000000000..9c075b815d --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ShardToken.java @@ -0,0 +1,39 @@ +package mage.game.permanent.token; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ + +public final class ShardToken extends TokenImpl { + + public ShardToken() { + super("Shard", "Shard token"); + cardType.add(CardType.ENCHANTMENT); + subtype.add(SubType.SHARD); + + // {2}, Sacrifice this enchantment: Scry 1, then draw a card. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(2)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); + SacrificeSourceCost cost = new SacrificeSourceCost(); + cost.setText("Sacrifice this enchantment"); + ability.addCost(cost); + this.addAbility(ability); + } + + public ShardToken(final ShardToken token) { + super(token); + } + + public ShardToken copy() { + return new ShardToken(this); + } +}