mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
* Nettling Impl - Fixed that the conditional delayed destroy ability did not work corretly (fixes #4142).
This commit is contained in:
parent
583033ff3b
commit
9e4beb6b51
3 changed files with 131 additions and 18 deletions
|
@ -30,13 +30,14 @@ package mage.cards.n;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
import mage.abilities.condition.InvertCondition;
|
import mage.abilities.condition.InvertCondition;
|
||||||
import mage.abilities.condition.common.TargetAttackedThisTurnCondition;
|
import mage.abilities.condition.common.TargetAttackedThisTurnCondition;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
import mage.abilities.decorator.ConditionalActivatedAbility;
|
||||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.DestroyTargetAtBeginningOfNextEndStepEffect;
|
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||||
import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect;
|
import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
@ -49,6 +50,7 @@ import mage.filter.predicate.permanent.ControllerPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
import mage.watchers.common.AttackedThisTurnWatcher;
|
import mage.watchers.common.AttackedThisTurnWatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,30 +58,33 @@ import mage.watchers.common.AttackedThisTurnWatcher;
|
||||||
* @author MTGfan
|
* @author MTGfan
|
||||||
*/
|
*/
|
||||||
public class NettlingImp extends CardImpl {
|
public class NettlingImp extends CardImpl {
|
||||||
|
|
||||||
final static FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Wall");
|
final static FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Wall");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(Predicates.not(new SubtypePredicate(SubType.WALL)));
|
filter.add(Predicates.not(new SubtypePredicate(SubType.WALL)));
|
||||||
filter.add(new ControlledFromStartOfControllerTurnPredicate());
|
filter.add(new ControlledFromStartOfControllerTurnPredicate());
|
||||||
filter.add(new ControllerPredicate(TargetController.ACTIVE));
|
filter.add(new ControllerPredicate(TargetController.ACTIVE));
|
||||||
filter.setMessage("non-Wall creature the active player has controlled continuously since the beginning of the turn.");
|
filter.setMessage("non-Wall creature the active player has controlled continuously since the beginning of the turn.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NettlingImp(UUID ownerId, CardSetInfo setInfo) {
|
public NettlingImp(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||||
|
|
||||||
this.subtype.add(SubType.IMP);
|
this.subtype.add(SubType.IMP);
|
||||||
this.power = new MageInt(1);
|
this.power = new MageInt(1);
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// {tap}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. Activate this ability only during an opponent's turn, before attackers are declared.
|
// {T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. Activate this ability only during an opponent's turn, before attackers are declared.
|
||||||
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AttacksIfAbleTargetEffect(Duration.EndOfTurn), new TapSourceCost(), new NettlingImpTurnCondition(), "{T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. Activate this ability only during an opponent's turn, before attackers are declared.");
|
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AttacksIfAbleTargetEffect(Duration.EndOfTurn),
|
||||||
ability.addEffect(new ConditionalOneShotEffect(new DestroyTargetAtBeginningOfNextEndStepEffect(), new InvertCondition(TargetAttackedThisTurnCondition.instance)));
|
new TapSourceCost(), new NettlingImpTurnCondition(),
|
||||||
|
"{T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. "
|
||||||
|
+ "That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. "
|
||||||
|
+ "Activate this ability only during an opponent's turn, before attackers are declared.");
|
||||||
|
ability.addEffect(new NettlingImpDelayedDestroyEffect());
|
||||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||||
this.addAbility(ability, new AttackedThisTurnWatcher());
|
this.addAbility(ability, new AttackedThisTurnWatcher());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NettlingImp(final NettlingImp card) {
|
public NettlingImp(final NettlingImp card) {
|
||||||
|
@ -96,7 +101,7 @@ class NettlingImpTurnCondition implements Condition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player activePlayer = game.getPlayer(game.getActivePlayerId());
|
Player activePlayer = game.getPlayer(game.getActivePlayerId());
|
||||||
return activePlayer != null && activePlayer.hasOpponent(source.getControllerId(), game) && game.getPhase().getStep().getType().getIndex() < 5;
|
return activePlayer != null && activePlayer.hasOpponent(source.getControllerId(), game) && game.getPhase().getStep().getType().getIndex() < 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,3 +110,32 @@ class NettlingImpTurnCondition implements Condition {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NettlingImpDelayedDestroyEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
public NettlingImpDelayedDestroyEffect() {
|
||||||
|
super(Outcome.Detriment);
|
||||||
|
this.staticText = "If it doesn't, destroy it at the beginning of the next end step";
|
||||||
|
}
|
||||||
|
|
||||||
|
public NettlingImpDelayedDestroyEffect(final NettlingImpDelayedDestroyEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NettlingImpDelayedDestroyEffect copy() {
|
||||||
|
return new NettlingImpDelayedDestroyEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
DestroyTargetEffect effect = new DestroyTargetEffect();
|
||||||
|
effect.setTargetPointer(new FixedTarget(source.getFirstTarget()));
|
||||||
|
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility
|
||||||
|
= new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.ALL, effect, TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance));
|
||||||
|
delayedAbility.getDuration();
|
||||||
|
delayedAbility.getTargets().addAll(source.getTargets());
|
||||||
|
game.addDelayedTriggeredAbility(delayedAbility, source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.mage.test.cards.triggers.delayed;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author LevelX2
|
||||||
|
*/
|
||||||
|
public class NettlingImplTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForcedDidAttack() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||||
|
|
||||||
|
// {T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. Activate this ability only during an opponent's turn, before attackers are declared.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Nettling Imp", 1);
|
||||||
|
|
||||||
|
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Choose", "Silvercoat Lion");
|
||||||
|
|
||||||
|
setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Nettling Imp", 1);
|
||||||
|
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||||
|
|
||||||
|
assertLife(playerA, 18);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForcedDidNotAttack() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||||
|
// {T}: Choose target non-Wall creature the active player has controlled continuously since the beginning of the turn. That creature attacks this turn if able. If it doesn't, destroy it at the beginning of the next end step. Activate this ability only during an opponent's turn, before attackers are declared.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Nettling Imp", 1);
|
||||||
|
// Tap target creature.
|
||||||
|
// Draw a card.
|
||||||
|
addCard(Zone.HAND, playerA, "Pressure Point", 1); // Instant {1}{W}
|
||||||
|
|
||||||
|
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Choose", "Silvercoat Lion");
|
||||||
|
castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, "Pressure Point", "Silvercoat Lion");
|
||||||
|
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Nettling Imp", 1);
|
||||||
|
assertGraveyardCount(playerA, "Pressure Point", 1);
|
||||||
|
assertPermanentCount(playerB, "Silvercoat Lion", 0);
|
||||||
|
|
||||||
|
assertHandCount(playerA, 2);
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,7 +28,9 @@
|
||||||
package mage.abilities.common.delayed;
|
package mage.abilities.common.delayed;
|
||||||
|
|
||||||
import mage.abilities.DelayedTriggeredAbility;
|
import mage.abilities.DelayedTriggeredAbility;
|
||||||
|
import mage.abilities.condition.Condition;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.constants.Duration;
|
||||||
import mage.constants.TargetController;
|
import mage.constants.TargetController;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
@ -42,6 +44,7 @@ import mage.game.permanent.Permanent;
|
||||||
public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
||||||
|
|
||||||
private TargetController targetController;
|
private TargetController targetController;
|
||||||
|
private Condition condition;
|
||||||
|
|
||||||
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect) {
|
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect) {
|
||||||
this(effect, TargetController.ANY);
|
this(effect, TargetController.ANY);
|
||||||
|
@ -52,14 +55,20 @@ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTrigg
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController) {
|
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController) {
|
||||||
super(effect);
|
this(zone, effect, targetController, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController, Condition condition) {
|
||||||
|
super(effect, Duration.EndOfTurn);
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
this.targetController = targetController;
|
this.targetController = targetController;
|
||||||
|
this.condition = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(final AtTheBeginOfNextEndStepDelayedTriggeredAbility ability) {
|
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(final AtTheBeginOfNextEndStepDelayedTriggeredAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.targetController = ability.targetController;
|
this.targetController = ability.targetController;
|
||||||
|
this.condition = ability.condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,27 +78,34 @@ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTrigg
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
boolean correctEndPhase = false;
|
||||||
switch (targetController) {
|
switch (targetController) {
|
||||||
case ANY:
|
case ANY:
|
||||||
return true;
|
correctEndPhase = true;
|
||||||
|
break;
|
||||||
case YOU:
|
case YOU:
|
||||||
return event.getPlayerId().equals(this.controllerId);
|
correctEndPhase = event.getPlayerId().equals(this.controllerId);
|
||||||
|
break;
|
||||||
case OPPONENT:
|
case OPPONENT:
|
||||||
if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) {
|
if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) {
|
||||||
return true;
|
correctEndPhase = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTROLLER_ATTACHED_TO:
|
case CONTROLLER_ATTACHED_TO:
|
||||||
Permanent attachment = game.getPermanent(sourceId);
|
Permanent attachment = game.getPermanent(sourceId);
|
||||||
if (attachment != null && attachment.getAttachedTo() != null) {
|
if (attachment != null && attachment.getAttachedTo() != null) {
|
||||||
Permanent attachedTo = game.getPermanent(attachment.getAttachedTo());
|
Permanent attachedTo = game.getPermanent(attachment.getAttachedTo());
|
||||||
if (attachedTo != null && attachedTo.getControllerId().equals(event.getPlayerId())) {
|
if (attachedTo != null && attachedTo.getControllerId().equals(event.getPlayerId())) {
|
||||||
return true;
|
correctEndPhase = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (correctEndPhase) {
|
||||||
|
if (condition != null && !condition.apply(game, this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue