Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DSK] Implement Nowhere to Run #13208

Merged
merged 5 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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");
}

}
Loading