From 24e73df5109e236d8ebe413fe0c580bd232f4421 Mon Sep 17 00:00:00 2001 From: Susucre <34709007+Susucre@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:28:00 +0200 Subject: [PATCH] implement [MH3] Thief of Existence --- .../src/mage/cards/t/ThiefOfExistence.java | 108 ++++++++++++++++++ Mage.Sets/src/mage/sets/ModernHorizons3.java | 1 + .../single/mh3/ThiefOfExistenceTest.java | 74 ++++++++++++ .../LeavesBattlefieldTriggeredAbility.java | 5 +- 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/t/ThiefOfExistence.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/ThiefOfExistenceTest.java diff --git a/Mage.Sets/src/mage/cards/t/ThiefOfExistence.java b/Mage.Sets/src/mage/cards/t/ThiefOfExistence.java new file mode 100644 index 00000000000..4ed3556df67 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThiefOfExistence.java @@ -0,0 +1,108 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DevoidAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class ThiefOfExistence extends CardImpl { + + private static final FilterPermanent filter = + new FilterPermanent("noncreature, nonland permanent an opponent controls with mana value 4 or less"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 4)); + } + + public ThiefOfExistence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{C}{G}"); + + this.subtype.add(SubType.ELDRAZI); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Devoid + this.addAbility(new DevoidAbility(this.color)); + + // When you cast this spell, exile up to one target noncreature, nonland permanent an opponent controls with mana value 4 or less. If you do, Thief of Existence gains "When this creature leaves the battlefield, target opponent draws a card." + Ability ability = new CastSourceTriggeredAbility(new ThiefOfExistenceTargetEffect()); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + this.addAbility(ability); + } + + private ThiefOfExistence(final ThiefOfExistence card) { + super(card); + } + + @Override + public ThiefOfExistence copy() { + return new ThiefOfExistence(this); + } +} + +class ThiefOfExistenceTargetEffect extends OneShotEffect { + + ThiefOfExistenceTargetEffect() { + super(Outcome.Exile); + staticText = "exile up to one target noncreature, nonland permanent an opponent controls with mana value 4 or less. " + + "If you do, Thief of Existence gains \"When this creature leaves the battlefield, target opponent draws a card\""; + } + + private ThiefOfExistenceTargetEffect(final ThiefOfExistenceTargetEffect effect) { + super(effect); + } + + @Override + public ThiefOfExistenceTargetEffect copy() { + return new ThiefOfExistenceTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + if (!permanent.moveToExile(null, null, source, game)) { + return true; + } + Spell sourceSpell = (Spell) source.getSourceCardIfItStillExists(game); + if (sourceSpell == null) { + return true; + } + // the target has been moved to exile. Thief gains the trigger on the stack (and will keep it on the battlefield) + TriggeredAbility trigger = new LeavesBattlefieldTriggeredAbility(new DrawCardTargetEffect(1)); + trigger.addTarget(new TargetOpponent()); + game.addEffect( + new GainAbilityTargetEffect(trigger, Duration.Custom, "", true) + .setTargetPointer(new FixedTarget(sourceSpell.getCard(), game)), + source + ); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3.java b/Mage.Sets/src/mage/sets/ModernHorizons3.java index 9e3c9f365a0..c38e7d5e135 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3.java @@ -289,6 +289,7 @@ public final class ModernHorizons3 extends ExpansionSet { cards.add(new SetCardInfo("The Creation of Avacyn", 86, Rarity.UNCOMMON, mage.cards.t.TheCreationOfAvacyn.class)); cards.add(new SetCardInfo("The Hunger Tide Rises", 158, Rarity.UNCOMMON, mage.cards.t.TheHungerTideRises.class)); cards.add(new SetCardInfo("The Necrobloom", 194, Rarity.RARE, mage.cards.t.TheNecrobloom.class)); + cards.add(new SetCardInfo("Thief of Existence", 174, Rarity.RARE, mage.cards.t.ThiefOfExistence.class)); cards.add(new SetCardInfo("Thraben Charm", 45, Rarity.COMMON, mage.cards.t.ThrabenCharm.class)); cards.add(new SetCardInfo("Thriving Skyclaw", 141, Rarity.COMMON, mage.cards.t.ThrivingSkyclaw.class)); cards.add(new SetCardInfo("Titans' Vanguard", 206, Rarity.UNCOMMON, mage.cards.t.TitansVanguard.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/ThiefOfExistenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/ThiefOfExistenceTest.java new file mode 100644 index 00000000000..79f08e1b429 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/ThiefOfExistenceTest.java @@ -0,0 +1,74 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ThiefOfExistenceTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.ThiefOfExistence Thief of Existence} {1}{C}{G} + * Creature — Eldrazi + * Devoid (This card has no color.) + * When you cast this spell, exile up to one target noncreature, nonland permanent an opponent controls with mana value 4 or less. If you do, Thief of Existence gains “When this creature leaves the battlefield, target opponent draws a card.” + * 3/4 + */ + private static final String thief = "Thief of Existence"; + + @Test + public void test_NoTarget_NoAddedTrigger() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, thief); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 1); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Bombardment", 1); + addCard(Zone.BATTLEFIELD, playerB, "Sylvan Library", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thief, true); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice", playerB); + setChoice(playerA, thief); // sacrifice thief to Bombardment + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerB, 20 - 1); + assertHandCount(playerB, 0); + assertGraveyardCount(playerA, thief, 1); + assertPermanentCount(playerB, 1); + } + + @Test + public void test_Target_AddedTrigger() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, thief); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 1); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Bombardment", 1); + addCard(Zone.BATTLEFIELD, playerB, "Sylvan Library", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thief, true); + addTarget(playerA, "Sylvan Library"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice", playerB); + setChoice(playerA, thief); // sacrifice Thief to Bombardment + addTarget(playerA, playerB); // thief has the added trigger + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerB, 20 - 1); + assertHandCount(playerB, 1); + assertGraveyardCount(playerA, thief, 1); + assertPermanentCount(playerB, 0); + assertExileCount(playerB, 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java index 7216e9b5a73..77dec49a46f 100644 --- a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java @@ -7,11 +7,14 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; /** - * * @author BetaSteward_at_googlemail.com */ public class LeavesBattlefieldTriggeredAbility extends ZoneChangeTriggeredAbility { + public LeavesBattlefieldTriggeredAbility(Effect effect) { + this(effect, false); + } + public LeavesBattlefieldTriggeredAbility(Effect effect, boolean optional) { super(Zone.ALL, Zone.BATTLEFIELD, null, effect, "When {this} leaves the battlefield, ", optional); }