Skip to content

Commit

Permalink
[DSK] Implement Nowhere to Run (#13208)
Browse files Browse the repository at this point in the history
* [DSK] implemented Nowhere to Run

* [DSK] implemented Nowhere to Run

* NowhereToRun - fixed typo in static test

* NowhereToRun - fixed hexproof effect
  • Loading branch information
markort147 authored Jan 9, 2025
1 parent 8772190 commit 30dac5c
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
128 changes: 128 additions & 0 deletions Mage.Sets/src/mage/cards/n/NowhereToRun.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}

}

0 comments on commit 30dac5c

Please sign in to comment.