diff --git a/Mage.Sets/src/mage/cards/n/NowhereToRun.java b/Mage.Sets/src/mage/cards/n/NowhereToRun.java new file mode 100644 index 000000000000..fd774a8f04b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NowhereToRun.java @@ -0,0 +1,128 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffect; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author markort147 + */ +public final class NowhereToRun extends CardImpl { + + public NowhereToRun(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Nowhere to Run enters, target creature an opponent controls gets -3/-3 until end of turn. + Ability etbAbility = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-3, -3, Duration.EndOfTurn)); + etbAbility.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(etbAbility); + + // Creatures your opponents control can be the targets of spells and abilities as though they didn't have hexproof. Ward abilities of those creatures don't trigger. + Ability staticAbility = new SimpleStaticAbility(new NowhereToRunHexproofEffect()); + staticAbility.addEffect(new NowhereToRunWardEffect()); + this.addAbility(staticAbility); + } + + private NowhereToRun(final NowhereToRun card) { + super(card); + } + + @Override + public NowhereToRun copy() { + return new NowhereToRun(this); + } +} + +class NowhereToRunHexproofEffect extends AsThoughEffectImpl { + + NowhereToRunHexproofEffect() { + super(AsThoughEffectType.HEXPROOF, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures your opponents control " + + "can be the targets of spells and " + + "abilities as though they didn't " + + "have hexproof."; + } + + private NowhereToRunHexproofEffect(final NowhereToRunHexproofEffect effect) { + super(effect); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + if (affectedControllerId.equals(source.getControllerId())) { + Permanent creature = game.getPermanent(sourceId); + return creature != null + && creature.isCreature(game) + && game.getOpponents(source.getControllerId()).contains(creature.getControllerId()); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public AsThoughEffect copy() { + return new NowhereToRunHexproofEffect(this); + } +} + +class NowhereToRunWardEffect extends ContinuousRuleModifyingEffectImpl { + + + NowhereToRunWardEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Ward abilities of those creatures don't trigger."; + } + + private NowhereToRunWardEffect(final NowhereToRunWardEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType().equals(GameEvent.EventType.NUMBER_OF_TRIGGERS); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent == null || !permanent.isCreature(game)) { + return false; + } + if (!game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) { + return false; + } + + return getValue("targetAbility") instanceof WardAbility; + } + + @Override + public ContinuousEffect copy() { + return new NowhereToRunWardEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index eb2e87f70c32..069107adced4 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -166,6 +166,7 @@ private DuskmournHouseOfHorror() { cards.add(new SetCardInfo("Neglected Manor", 264, Rarity.COMMON, mage.cards.n.NeglectedManor.class)); cards.add(new SetCardInfo("Niko, Light of Hope", 224, Rarity.MYTHIC, mage.cards.n.NikoLightOfHope.class)); cards.add(new SetCardInfo("Norin, Swift Survivalist", 145, Rarity.UNCOMMON, mage.cards.n.NorinSwiftSurvivalist.class)); + cards.add(new SetCardInfo("Nowhere to Run", 111, Rarity.UNCOMMON, mage.cards.n.NowhereToRun.class)); cards.add(new SetCardInfo("Oblivious Bookworm", 225, Rarity.UNCOMMON, mage.cards.o.ObliviousBookworm.class)); cards.add(new SetCardInfo("Omnivorous Flytrap", 192, Rarity.RARE, mage.cards.o.OmnivorousFlytrap.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Omnivorous Flytrap", 322, Rarity.RARE, mage.cards.o.OmnivorousFlytrap.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/NowhereToRunTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/NowhereToRunTest.java new file mode 100644 index 000000000000..abef16e97386 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/NowhereToRunTest.java @@ -0,0 +1,106 @@ +package org.mage.test.cards.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author markort147 + */ +public class NowhereToRunTest extends CardTestPlayerBase { + + // Prevent ward from triggering on opponent's creatures + @Test + public void testWardPreventingOnOpponentsCreatures() { + + addCard(Zone.BATTLEFIELD, playerB, "Waterfall Aerialist", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.HAND, playerA, "Nowhere to Run", 1); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nowhere to Run"); + addTarget(playerA, "Waterfall Aerialist"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Waterfall Aerialist", 0); + } + + // Does not prevent ward from triggering on own creatures + @Test + public void testWardOnOwnCreatures() { + + addCard(Zone.BATTLEFIELD, playerA, "Waterfall Aerialist", 1); + addCard(Zone.BATTLEFIELD, playerA, "Nowhere to Run", 1); + addCard(Zone.HAND, playerB, "Swords to Plowshares", 1); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Swords to Plowshares"); + addTarget(playerB, "Waterfall Aerialist"); + setChoice(playerB, false); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Waterfall Aerialist", 1); + assertGraveyardCount(playerB, "Swords to Plowshares", 1); + } + + // Prevent hexproof on opponent's creatures + @Test + public void testHexproofOnCreatures() { + + addCard(Zone.BATTLEFIELD, playerB, "Gladecover Scout", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Nowhere to Run", 1); + addCard(Zone.HAND, playerA, "Go for the Throat", 1); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Go for the Throat"); + addTarget(playerA, "Gladecover Scout"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Gladecover Scout", 0); + } + + // Does not prevent hexproof on non-creature permanents + @Test + public void testHexproofOnOtherPermanents() { + + addCard(Zone.BATTLEFIELD, playerB, "Valgavoth's Lair", 1); + setChoice(playerB, "Red"); + + addCard(Zone.BATTLEFIELD, playerA, "Nowhere to Run", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Stone Rain", 1); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stone Rain"); + addTarget(playerA, "Valgavoth's Lair"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + try { + execute(); + } catch (Throwable e) { + if (!e.getMessage().contains("Targets list was setup by addTarget with [Valgavoth's Lair], but not used")) { + Assert.fail("must throw error about bad targets, but got:\n" + e.getMessage()); + } + return; + } + Assert.fail("must throw exception on execute"); + } + +}