diff --git a/Mage.Sets/src/mage/cards/e/ErtaisFamiliar.java b/Mage.Sets/src/mage/cards/e/ErtaisFamiliar.java new file mode 100644 index 00000000000..bb0b26348ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ErtaisFamiliar.java @@ -0,0 +1,100 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SourcePhaseOutTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.keyword.PhasingAbility; +import mage.abilities.meta.OrTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class ErtaisFamiliar extends CardImpl { + + public ErtaisFamiliar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Phasing + this.addAbility(PhasingAbility.getInstance()); + + // When Ertai's Familiar phases out or leaves the battlefield, mill three cards. + TriggeredAbility trigger = new OrTriggeredAbility( + Zone.BATTLEFIELD, + new MillCardsControllerEffect(3), + new SourcePhaseOutTriggeredAbility(null, false), + new LeavesBattlefieldTriggeredAbility(null, false) + ).setTriggerPhrase("When {this} phases out or leaves the battlefield, "); + trigger.setWorksPhasedOut(true); + this.addAbility(trigger); + + // {U}: Until your next upkeep, Ertai's Familiar can't phase out. + this.addAbility(new SimpleActivatedAbility( + new ErtaisFamiliarReplacementEffect(), + new ManaCostsImpl<>("{U}") + )); + } + + private ErtaisFamiliar(final ErtaisFamiliar card) { + super(card); + } + + @Override + public ErtaisFamiliar copy() { + return new ErtaisFamiliar(this); + } +} + +class ErtaisFamiliarReplacementEffect extends ContinuousRuleModifyingEffectImpl { + + public ErtaisFamiliarReplacementEffect() { + super(Duration.UntilYourNextUpkeepStep, Outcome.Neutral); + staticText = "until your next upkeep, {this} can't phase out"; + } + + private ErtaisFamiliarReplacementEffect(final ErtaisFamiliarReplacementEffect effect) { + super(effect); + } + + @Override + public ErtaisFamiliarReplacementEffect copy() { + return new ErtaisFamiliarReplacementEffect(this); + } + + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PHASE_OUT; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + affectedObjectList.clear(); + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + affectedObjectsSet = true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + MageObjectReference targetMOR = new MageObjectReference(event.getTargetId(), game); + return affectedObjectList.stream().anyMatch(mor -> mor.equals(targetMOR)); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java index ad5e41d063a..a65eb43f367 100644 --- a/Mage.Sets/src/mage/sets/Weatherlight.java +++ b/Mage.Sets/src/mage/sets/Weatherlight.java @@ -80,6 +80,7 @@ public final class Weatherlight extends ExpansionSet { cards.add(new SetCardInfo("Dwarven Berserker", 97, Rarity.COMMON, mage.cards.d.DwarvenBerserker.class)); cards.add(new SetCardInfo("Dwarven Thaumaturgist", 98, Rarity.RARE, mage.cards.d.DwarvenThaumaturgist.class)); cards.add(new SetCardInfo("Empyrial Armor", 13, Rarity.COMMON, mage.cards.e.EmpyrialArmor.class)); + cards.add(new SetCardInfo("Ertai's Familiar", 38, Rarity.RARE, mage.cards.e.ErtaisFamiliar.class)); cards.add(new SetCardInfo("Fallow Wurm", 126, Rarity.UNCOMMON, mage.cards.f.FallowWurm.class)); cards.add(new SetCardInfo("Familiar Ground", 127, Rarity.UNCOMMON, mage.cards.f.FamiliarGround.class)); cards.add(new SetCardInfo("Fatal Blow", 67, Rarity.COMMON, mage.cards.f.FatalBlow.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/wth/ErtaisFamiliarTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/wth/ErtaisFamiliarTest.java new file mode 100644 index 00000000000..cc35400ba05 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/wth/ErtaisFamiliarTest.java @@ -0,0 +1,106 @@ + +package org.mage.test.cards.single.wth; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ErtaisFamiliarTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.e.ErtaisFamiliar}
+ * Ertai's Familiar {1}{U}
+ * Creature — Illusion
+ * Phasing
+ * When Ertai’s Familiar phases out or leaves the battlefield, mill three cards.
+ * {U}: Until your next upkeep, Ertai’s Familiar can’t phase out.
+ * 2/2 + */ + private final String familiar = "Ertai's Familiar"; + + @Test + public void test_phaseout() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, familiar); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + // Casting the familiar for it to not start phasing out turn 1. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, familiar); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, 3); + assertPermanentCount(playerA, familiar, 0); + } + + @Test + public void test_activate_cant_phaseout() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, familiar); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + // Casting the familiar for it to not start phasing out turn 1. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, familiar); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{U}:"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, 0); + assertPermanentCount(playerA, familiar, 1); + } + + @Test + public void test_activate_cant_phaseout_other_effect() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, familiar); + // Put a +1/+1 counter on target creature. It phases out. + addCard(Zone.HAND, playerA, "Slip Out the Back"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + // Casting the familiar for it to not start phasing out turn 1. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, familiar); + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{U}:"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Slip Out the Back", familiar); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, 1); // Slip Out the Back + assertPermanentCount(playerA, familiar, 1); + assertPowerToughness(playerA, familiar, 3, 3); + } + + @Test + public void test_activate_cant_phaseout_then_blink() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, familiar); + // Exile target creature you control, then return that card to the battlefield under its owner’s control. + // Draw a card. + addCard(Zone.HAND, playerA, "Blur"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + + // Casting the familiar for it to not start phasing out turn 1. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, familiar); + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{U}:"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Blur", familiar); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, 3 * 2 + 1); // 2 familiar trigger + Blur + assertPermanentCount(playerA, familiar, 0); + } +} \ No newline at end of file