From b9511afa50317ba3311e49b34ad0e78030ca2672 Mon Sep 17 00:00:00 2001 From: Susucre <34709007+Susucre@users.noreply.github.com> Date: Sat, 4 May 2024 19:27:24 +0200 Subject: [PATCH] implement [MH3] Phelia, Exuberant Shepherd --- .../mage/cards/p/PheliaExuberantShepherd.java | 121 ++++++++++++++ Mage.Sets/src/mage/sets/ModernHorizons3.java | 1 + .../mh3/PheliaExuberantShepherdTest.java | 152 ++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java diff --git a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java new file mode 100644 index 00000000000..46eee69a66c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java @@ -0,0 +1,121 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author Susucr + */ +public final class PheliaExuberantShepherd extends CardImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent("other target nonland permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + public PheliaExuberantShepherd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever Phelia, Exuberant Shepherd attacks, exile up to one other target nonland permanent. At the beginning of the next end step, return that card to the battlefield under its owner's control. If it entered under your control, put a +1/+1 counter on Phelia. + Ability ability = new AttacksTriggeredAbility(new ExileTargetEffect().setToSourceExileZone(true)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new PheliaExuberantShepherdEffect()), false + )); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability); + } + + private PheliaExuberantShepherd(final PheliaExuberantShepherd card) { + super(card); + } + + @Override + public PheliaExuberantShepherd copy() { + return new PheliaExuberantShepherd(this); + } +} + +class PheliaExuberantShepherdEffect extends OneShotEffect { + + PheliaExuberantShepherdEffect() { + super(Outcome.Benefit); + staticText = "return that card to the battlefield under its owner's control. " + + "If it entered under your control, put a +1/+1 counter on {this}"; + } + + private PheliaExuberantShepherdEffect(final PheliaExuberantShepherdEffect effect) { + super(effect); + } + + @Override + public PheliaExuberantShepherdEffect copy() { + return new PheliaExuberantShepherdEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + UUID zoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ExileZone exileZone = game.getExile().getExileZone(zoneId); + if (exileZone == null || exileZone.isEmpty()) { + return false; + } + Set cards = exileZone.getCards(game); + player.moveCards(cards, Zone.BATTLEFIELD, source, game, false, false, true, null); + game.getState().applyEffects(game); + + Permanent phelia = source.getSourcePermanentIfItStillExists(game); + if (phelia == null) { + return true; // If phelia is no longer on the battlefield, the +1/+1 part of the effect does not apply. + } + boolean enteredUnderYourControl = false; + for (Card card : cards) { + // Try to find the permanent that card became + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) { + enteredUnderYourControl = true; + break; + } + } + if (!enteredUnderYourControl) { + return true; + } + phelia.addCounters(CounterType.P1P1.createInstance(), source, game); + 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 381a84c0828..9391cae08ea 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3.java @@ -40,6 +40,7 @@ public final class ModernHorizons3 extends ExpansionSet { cards.add(new SetCardInfo("Null Elemental Blast", 12, Rarity.UNCOMMON, mage.cards.n.NullElementalBlast.class)); cards.add(new SetCardInfo("Nulldrifter", 13, Rarity.RARE, mage.cards.n.Nulldrifter.class)); cards.add(new SetCardInfo("Orim's Chant", 265, Rarity.RARE, mage.cards.o.OrimsChant.class)); + cards.add(new SetCardInfo("Phelia, Exuberant Shepherd", 40, Rarity.RARE, mage.cards.p.PheliaExuberantShepherd.class)); cards.add(new SetCardInfo("Plains", 304, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Polluted Delta", 224, Rarity.RARE, mage.cards.p.PollutedDelta.class)); cards.add(new SetCardInfo("Priest of Titania", 286, Rarity.UNCOMMON, mage.cards.p.PriestOfTitania.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java new file mode 100644 index 00000000000..1abb61f00cf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java @@ -0,0 +1,152 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class PheliaExuberantShepherdTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.p.PheliaExuberantShepherd Phelia, Exuberant Sheperd} {1}{W} + * Legendary Creature — Dog + * Flash + * Whenever Phelia, Exuberant Shepherd attacks, exile up to one other target nonland permanent. At the beginning of the next end step, return that card to the battlefield under its owner’s control. If it entered under your control, put a +1/+1 counter on Phelia. + * 2/2 + */ + private static final String phelia = "Phelia, Exuberant Shepherd"; + + @Test + public void test_Simple_Opponent() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 1); + assertPowerToughness(playerA, phelia, 2 + 0, 2 + 0); + assertPermanentCount(playerB, "Memnite", 1); + } + + @Test + public void test_Simple_Owned() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 1); + assertPowerToughness(playerA, phelia, 2 + 1, 2 + 1); + assertPermanentCount(playerA, "Memnite", 1); + } + + @Test + public void test_Killed_OnTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.HAND, playerA, "Doom Blade", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Doom Blade", phelia); // the attack trigger on the stack. + + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 0); + assertPermanentCount(playerA, "Memnite", 1); + } + + @Test + public void test_Killed_AfterTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.HAND, playerA, "Doom Blade", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + + castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Doom Blade", phelia); + + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 0); + assertPermanentCount(playerA, "Memnite", 1); + } + + @Test + public void test_Blink_OnTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Ephemerate", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Ephemerate", phelia); // the attack trigger on the stack. + + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 1); + assertPowerToughness(playerA, phelia, 2 + 0, 2 + 0); + assertPermanentCount(playerA, "Memnite", 1); + } + + @Test + public void test_Blink_AfterTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Ephemerate", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + + castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ephemerate", phelia); + + checkExileCount("Memnite got exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, phelia, 1); + assertPowerToughness(playerA, phelia, 2 + 0, 2 + 0); + assertPermanentCount(playerA, "Memnite", 1); + } + +}