diff --git a/Mage.Sets/src/mage/cards/t/Takklemaggot.java b/Mage.Sets/src/mage/cards/t/Takklemaggot.java new file mode 100644 index 00000000000..55589fc996a --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/Takklemaggot.java @@ -0,0 +1,231 @@ +package mage.cards.t; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CanBeEnchantedByPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author xenohedron + */ +public final class Takklemaggot extends CardImpl { + + public Takklemaggot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // At the beginning of the upkeep of enchanted creature's controller, put a -0/-1 counter on that creature. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersAttachedEffect(CounterType.M0M1.createInstance(), "that creature"), + TargetController.CONTROLLER_ATTACHED_TO, false, false)); + + // When enchanted creature dies, that creature's controller chooses a creature that Takklemaggot could enchant. + // If they do, return Takklemaggot to the battlefield under your control attached to that creature. + // If they don't, return Takklemaggot to the battlefield under your control as a non-Aura enchantment. + // It loses "enchant creature" and gains "At the beginning of that player's upkeep, Takklemaggot deals 1 damage to that player." + this.addAbility(new DiesAttachedTriggeredAbility(new TakklemaggotEffect(), "enchanted creature", + false, true, SetTargetPointer.ATTACHED_TO_CONTROLLER)); + } + + private Takklemaggot(final Takklemaggot card) { + super(card); + } + + @Override + public Takklemaggot copy() { + return new Takklemaggot(this); + } + +} + +class TakklemaggotEffect extends OneShotEffect { + + TakklemaggotEffect() { + super(Outcome.PutCardInPlay); + staticText = "that creature's controller chooses a creature that {this} could enchant. " + + "If the player does, return {this} to the battlefield under your control attached to that creature. " + + "If they don't, return {this} to the battlefield under your control as a non-Aura enchantment. " + + "It loses \"enchant creature\" and gains \"At the beginning of that player's upkeep, {this} deals 1 damage to that player.\""; + } + + private TakklemaggotEffect(final TakklemaggotEffect effect) { + super(effect); + } + + @Override + public TakklemaggotEffect copy() { + return new TakklemaggotEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + Card auraCard = game.getCard(source.getSourceId()); + if (controller == null || player == null || auraCard == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new CanBeEnchantedByPredicate(auraCard)); + Target target = new TargetCreaturePermanent(filter).withNotTarget(true); + if (!game.getBattlefield().getActivePermanents(filter, player.getId(), source, game).isEmpty() + && player.choose(outcome, target, source, game)) { + // return attached to that creature + Permanent creature = game.getPermanent(target.getFirstTarget()); + if (creature == null) { + return false; + } + game.getState().setValue("attachTo:" + auraCard.getId(), creature); + controller.moveCards(auraCard, Zone.BATTLEFIELD, source, game); + creature.addAttachment(auraCard.getId(), source, game); + } else { + // return as non-Aura enchantment + game.addEffect(new TakklemaggotNonAuraEffect(player.getId()), source); + controller.moveCards(auraCard, Zone.BATTLEFIELD, source, game); + auraCard.addInfo("chosen player", CardUtil.addToolTipMarkTags("Chosen player: " + player.getLogName()), game); + } + return true; + } + +} + +class TakklemaggotNonAuraEffect extends ContinuousEffectImpl { + + private final UUID playerId; + + TakklemaggotNonAuraEffect(UUID playerId) { + super(Duration.Custom, Outcome.AddAbility); + this.playerId = playerId; + } + + private TakklemaggotNonAuraEffect(final TakklemaggotNonAuraEffect effect) { + super(effect); + this.playerId = effect.playerId; + } + + @Override + public TakklemaggotNonAuraEffect copy() { + return new TakklemaggotNonAuraEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent; + if (affectedObjectList.isEmpty()) { // not yet initiated, check entering permanent + permanent = game.getPermanentEntering(source.getSourceId()); + // if an entering permanent is found, then proceed to applying continuous effects by layer + // otherwise try to find the permanent on the battlefield + if (permanent == null) { + permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + discard(); // no permanent found, can't initiate + return false; + } else { + // initiate with ZCC and check that in all future calls + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + } + } + } else { + permanent = affectedObjectList.get(0).getPermanent(game); + if (permanent == null) { + discard(); // permanent no longer on battlefield so effect no longer applies + return true; + } + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeSubType(game, SubType.AURA); + return true; + case AbilityAddingRemovingEffects_6: + List toRemove = new ArrayList<>(); + for (Ability ability : permanent.getAbilities(game)) { + if (ability instanceof EnchantAbility) { + toRemove.add(ability); + } + } + permanent.removeAbilities(toRemove, source.getSourceId(), game); + permanent.addAbility(new TakklemaggotUpkeepAbility(playerId), source.getSourceId(), game); + return true; + default: + return true; + } + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return Layer.AbilityAddingRemovingEffects_6 == layer || Layer.TypeChangingEffects_4 == layer; + } + +} + +class TakklemaggotUpkeepAbility extends TriggeredAbilityImpl { + + private final UUID playerId; + + TakklemaggotUpkeepAbility(UUID playerId) { + super(Zone.BATTLEFIELD, new DamageTargetEffect(1, true, "that player") + .setTargetPointer(new FixedTarget(playerId)), false); + this.playerId = playerId; + setTriggerPhrase("At the beginning of that player's upkeep, "); + } + + private TakklemaggotUpkeepAbility(final TakklemaggotUpkeepAbility ability) { + super(ability); + this.playerId = ability.playerId; + } + + @Override + public TakklemaggotUpkeepAbility copy() { + return new TakklemaggotUpkeepAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(this.playerId); + } + +} diff --git a/Mage.Sets/src/mage/sets/Chronicles.java b/Mage.Sets/src/mage/sets/Chronicles.java index abfa44a566a..8691fb76eb9 100644 --- a/Mage.Sets/src/mage/sets/Chronicles.java +++ b/Mage.Sets/src/mage/sets/Chronicles.java @@ -118,6 +118,7 @@ public final class Chronicles extends ExpansionSet { cards.add(new SetCardInfo("Sol'kanar the Swamp King", 85, Rarity.RARE, mage.cards.s.SolkanarTheSwampKing.class)); cards.add(new SetCardInfo("Stangg", 86, Rarity.RARE, mage.cards.s.Stangg.class)); cards.add(new SetCardInfo("Storm Seeker", 70, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); + cards.add(new SetCardInfo("Takklemaggot", 37, Rarity.UNCOMMON, mage.cards.t.Takklemaggot.class)); cards.add(new SetCardInfo("Teleport", 26, Rarity.RARE, mage.cards.t.Teleport.class)); cards.add(new SetCardInfo("The Fallen", 38, Rarity.UNCOMMON, mage.cards.t.TheFallen.class)); cards.add(new SetCardInfo("The Wretched", 39, Rarity.RARE, mage.cards.t.TheWretched.class)); diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index b61739ac0af..2800ea8176c 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -273,6 +273,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Sylvan Library", 207, Rarity.UNCOMMON, mage.cards.s.SylvanLibrary.class)); cards.add(new SetCardInfo("Sylvan Paradise", 208, Rarity.UNCOMMON, mage.cards.s.SylvanParadise.class)); cards.add(new SetCardInfo("Syphon Soul", 118, Rarity.COMMON, mage.cards.s.SyphonSoul.class)); + cards.add(new SetCardInfo("Takklemaggot", 119, Rarity.UNCOMMON, mage.cards.t.Takklemaggot.class)); cards.add(new SetCardInfo("Telekinesis", 79, Rarity.RARE, mage.cards.t.Telekinesis.class)); cards.add(new SetCardInfo("Teleport", 80, Rarity.RARE, mage.cards.t.Teleport.class)); cards.add(new SetCardInfo("Tetsuo Umezawa", 262, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionIII.java b/Mage.Sets/src/mage/sets/MastersEditionIII.java index fcc4435412a..481db42e98c 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIII.java @@ -218,6 +218,7 @@ public final class MastersEditionIII extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 223, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 224, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sword of the Ages", 202, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); + cards.add(new SetCardInfo("Takklemaggot", 76, Rarity.UNCOMMON, mage.cards.t.Takklemaggot.class)); cards.add(new SetCardInfo("Tetsuo Umezawa", 179, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); cards.add(new SetCardInfo("The Abyss", 77, Rarity.RARE, mage.cards.t.TheAbyss.class)); cards.add(new SetCardInfo("The Lady of the Mountain", 180, Rarity.COMMON, mage.cards.t.TheLadyOfTheMountain.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/TakklemaggotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/TakklemaggotTest.java new file mode 100644 index 00000000000..70347474819 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/TakklemaggotTest.java @@ -0,0 +1,50 @@ +package org.mage.test.cards.single.leg; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class TakklemaggotTest extends CardTestPlayerBase { + + private static final String takklemaggot = "Takklemaggot"; // 2BB Aura + /* Enchant creature + At the beginning of the upkeep of enchanted creature’s controller, put a -0/-1 counter on that creature. + When enchanted creature dies, that creature’s controller chooses a creature that Takklemaggot could enchant. + If the player does, return Takklemaggot to the battlefield under your control attached to that creature. + If they don’t, return Takklemaggot to the battlefield under your control as a non-Aura enchantment. + It loses “enchant creature” and gains “At the beginning of that player’s upkeep, Takklemaggot deals 1 damage to that player.” + */ + + @Test + public void testTakklemaggot() { + addCard(Zone.HAND, playerA, takklemaggot, 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.BATTLEFIELD, playerB, "Harvest Hand", 1); // 2/2 returns as equipment "Scrounged Scythe" + addCard(Zone.BATTLEFIELD, playerB, "White Knight", 1); // 2/2 protection from black + addCard(Zone.BATTLEFIELD, playerB, "Mist Leopard", 1); // 3/2 shroud + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, takklemaggot, "Harvest Hand"); + + checkPT("t1", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Harvest Hand", 2,2); + checkPT("t2", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Harvest Hand", 2, 1); + checkPT("t3", 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Harvest Hand", 2, 1); + setChoice(playerB, "Mist Leopard"); + checkPermanentCount("equipment", 4, PhaseStep.POSTCOMBAT_MAIN, playerB, "Scrounged Scythe", 1); + checkPermanentCount("takklemaggot", 4, PhaseStep.POSTCOMBAT_MAIN, playerA, takklemaggot, 1); + checkPT("t5", 5, PhaseStep.PRECOMBAT_MAIN, playerB, "Mist Leopard", 3, 2); + checkPT("t6", 6, PhaseStep.PRECOMBAT_MAIN, playerB, "Mist Leopard", 3, 1); + checkPT("t7", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Mist Leopard", 3, 1); + checkGraveyardCount("leopard", 8, PhaseStep.POSTCOMBAT_MAIN, playerB, "Mist Leopard", 1); + checkLife("t9", 9, PhaseStep.PRECOMBAT_MAIN, playerB, 20); + checkLife("t10", 10, PhaseStep.PRECOMBAT_MAIN, playerB, 19); + checkLife("t11", 11, PhaseStep.PRECOMBAT_MAIN, playerB, 19); + + setStopAt(12, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + + assertLife(playerB, 18); + } +}