1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-03-30 01:03:57 -09:00

Fixed handling of DontUntapInControllersNextUntapStepTargetEffect (fixes Sleep bug).

This commit is contained in:
LevelX2 2016-03-14 18:15:57 +01:00
parent c85cc9cb98
commit 272f68b530
11 changed files with 169 additions and 171 deletions
Mage.Sets/src/mage/sets
Mage.Tests/src/test/java/org/mage/test/combat
Mage/src/main/java/mage/abilities/effects/common

View file

@ -152,12 +152,10 @@ class GuardianOfTazeemEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
Permanent targetCreature = game.getPermanent(source.getFirstTarget());
if (land != null && targetCreature != null) {
if (land.hasSubtype("Island")) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("that creature");
effect.setTargetPointer(new FixedTarget(targetCreature, game));
game.addEffect(effect, source);
}
if (land != null && targetCreature != null && land.hasSubtype("Island")) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("that creature");
effect.setTargetPointer(new FixedTarget(targetCreature, game));
game.addEffect(effect, source);
}
return true;
}

View file

@ -27,13 +27,15 @@
*/
package mage.sets.commander;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DoIfClashWonEffect;
import mage.abilities.effects.common.PreventAllDamageByAllEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.PreventAllDamageByAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
@ -43,7 +45,7 @@ import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
*
@ -55,7 +57,6 @@ public class PollenLullaby extends CardImpl {
super(ownerId, 26, "Pollen Lullaby", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}");
this.expansionSetCode = "CMD";
// Prevent all combat damage that would be dealt this turn. Clash with an opponent. If you win, creatures that player controls don't untap during the player's next untap step.
this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn, true));
this.getSpellAbility().addEffect(new DoIfClashWonEffect(new PollenLullabyEffect(), true, null));
@ -86,10 +87,13 @@ class PollenLullabyEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
for (Permanent creature: game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) {
creature.tap(game);
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) {
doNotUntapNextUntapStep.add(creature);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTarget(creature.getId()));
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;
@ -103,4 +107,3 @@ class PollenLullabyEffect extends OneShotEffect {
}
}

View file

@ -27,6 +27,8 @@
*/
package mage.sets.commander2014;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
@ -46,7 +48,7 @@ import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
import mage.watchers.common.CastFromHandWatcher;
/**
@ -105,10 +107,14 @@ class BreachingLeviathanEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
creature.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));
doNotUntapNextUntapStep.add(creature);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;

View file

@ -27,27 +27,25 @@
*/
package mage.sets.darkascension;
import mage.constants.CardType;
import mage.constants.Rarity;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.common.FatefulHourCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.PreventAllDamageByAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.PhaseStep;
import mage.constants.Rarity;
import mage.filter.common.FilterAttackingCreature;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
*
@ -59,7 +57,6 @@ public class ClingingMists extends CardImpl {
super(ownerId, 109, "Clinging Mists", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{G}");
this.expansionSetCode = "DKA";
// Prevent all combat damage that would be dealt this turn.
this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(null, Duration.EndOfTurn, true));
@ -94,11 +91,15 @@ class ClingingMistsEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
for (Permanent creature: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
creature.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));
game.addEffect(effect, source);
doNotUntapNextUntapStep.add(creature);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;
}

View file

@ -27,12 +27,14 @@
*/
package mage.sets.invasion;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.PreventAllDamageByAllEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.PreventAllDamageByAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
@ -43,7 +45,7 @@ import mage.filter.predicate.permanent.AttackingPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
*
@ -55,7 +57,6 @@ public class Tangle extends CardImpl {
super(ownerId, 213, "Tangle", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{G}");
this.expansionSetCode = "INV";
// Prevent all combat damage that would be dealt this turn.
this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn, true));
// Each attacking creature doesn't untap during its controller's next untap step.
@ -99,9 +100,13 @@ class TangleEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
for (Permanent permanent :game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(permanent.getId()));
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
doNotUntapNextUntapStep.add(permanent);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;

View file

@ -1,16 +1,16 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,29 +20,30 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.magic2010;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
*
@ -83,11 +84,16 @@ class SleepEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
for (Permanent creature: game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) {
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) {
creature.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));
game.addEffect(effect, source);
doNotUntapNextUntapStep.add(creature);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("", player.getId());
effect.setText("those creatures don't untap during that player's next untap step");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;
}

View file

@ -27,6 +27,8 @@
*/
package mage.sets.portalthreekingdoms;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
@ -43,7 +45,7 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
*
@ -55,7 +57,6 @@ public class Exhaustion extends CardImpl {
super(ownerId, 42, "Exhaustion", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{U}");
this.expansionSetCode = "PTK";
// Creatures and lands target opponent controls don't untap during his or her next untap step.
this.getSpellAbility().addEffect(new ExhaustionEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
@ -72,35 +73,39 @@ public class Exhaustion extends CardImpl {
}
class ExhaustionEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(Predicates.or(new CardTypePredicate(CardType.CREATURE), new CardTypePredicate(CardType.LAND)));
}
ExhaustionEffect() {
super(Outcome.Detriment);
this.staticText = "Creatures and lands target opponent controls don't untap during his or her next untap step.";
}
ExhaustionEffect(final ExhaustionEffect effect) {
super(effect);
}
@Override
public ExhaustionEffect copy() {
return new ExhaustionEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, player.getId(), game)) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
doNotUntapNextUntapStep.add(permanent);
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
return true;
}

View file

@ -27,8 +27,10 @@
*/
package mage.sets.theros;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -52,7 +54,7 @@ import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil;
import mage.watchers.Watcher;
@ -69,7 +71,7 @@ public class TritonTactics extends CardImpl {
// Up to two target creatures each get +0/+3 until end of turn. Untap those creatures.
// At this turn's next end of combat, tap each creature that was blocked by one of those
// creatures this turn and it doesn't untap during its controller's next untap step.
Effect effect = new BoostTargetEffect(0,3, Duration.EndOfTurn);
Effect effect = new BoostTargetEffect(0, 3, Duration.EndOfTurn);
effect.setText("Up to two target creatures each get +0/+3 until end of turn");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
@ -78,7 +80,6 @@ public class TritonTactics extends CardImpl {
this.getSpellAbility().addWatcher(new BlockedCreaturesWatcher());
}
public TritonTactics(final TritonTactics card) {
@ -110,7 +111,7 @@ class TritonTacticsUntapTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Set<String> targets = new HashSet<>();
for (UUID target: targetPointer.getTargets(game, source)) {
for (UUID target : targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(target);
if (permanent != null) {
permanent.untap(game);
@ -189,17 +190,21 @@ class TritonTacticsEndOfCombatEffect extends OneShotEffect {
Object object = game.getState().getValue("blockedAttackers" + source.getSourceId());
if (object != null && object instanceof Map) {
attackerMap = (Map<Integer, Set<String>>) object;
for (Set<String> attackerSet :attackerMap.values()) {
for (Set<String> attackerSet : attackerMap.values()) {
List<Permanent> doNotUntapNextUntapStep = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), game)) {
if (attackerSet.contains(CardUtil.getCardZoneString(null, creature.getId(), game))) {
// tap creature and add the not untap effect
creature.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));
game.addEffect(effect, source);
doNotUntapNextUntapStep.add(creature);
game.informPlayers(new StringBuilder("Triton Tactics: ").append(creature.getName()).append(" doesn't untap during its controller's next untap step").toString());
}
}
if (!doNotUntapNextUntapStep.isEmpty()) {
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature");
effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game));
game.addEffect(effect, source);
}
}
}
if (attackerMap != null) {

View file

@ -28,25 +28,18 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.cards.CardImpl;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
@ -66,7 +59,11 @@ public class LorthosTheTidemaker extends CardImpl {
this.toughness = new MageInt(8);
// Whenever Lorthos, the Tidemaker attacks, you may pay {8}. If you do, tap up to eight target permanents. Those permanents don't untap during their controllers' next untap steps.
AttacksTriggeredAbility ability = new AttacksTriggeredAbility(new LorthosTheTideMakerEffect(), true);
DoIfCostPaid effect = new DoIfCostPaid(new TapTargetEffect(), new GenericManaCost(8), "Pay {8} to tap up to 8 target permanents? (They don't untap during their controllers' next untap steps)");
AttacksTriggeredAbility ability = new AttacksTriggeredAbility(effect, false);
Effect effect2 = new DontUntapInControllersNextUntapStepTargetEffect();
effect2.setText("Those permanents don't untap during their controllers' next untap steps");
effect.addEffect(effect2);
ability.addTarget(new TargetPermanent(0, 8, filter, false));
this.addAbility(ability);
}
@ -80,44 +77,3 @@ public class LorthosTheTidemaker extends CardImpl {
return new LorthosTheTidemaker(this);
}
}
class LorthosTheTideMakerEffect extends OneShotEffect {
public LorthosTheTideMakerEffect() {
super(Outcome.Tap);
this.staticText = "you may pay {8}. If you do, tap up to eight target permanents. Those permanents don't untap during their controllers' next untap steps";
}
public LorthosTheTideMakerEffect(final LorthosTheTideMakerEffect effect) {
super(effect);
}
@Override
public LorthosTheTideMakerEffect copy() {
return new LorthosTheTideMakerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Cost cost = new ManaCostsImpl("{8}");
if (player.chooseUse(Outcome.Tap, "Pay " + cost.getText() + " and " + staticText, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
for (UUID target : this.targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(target);
if (permanent != null) {
permanent.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
}
}
return false;
}
}
}
return false;
}
}

View file

@ -1,6 +1,5 @@
package org.mage.test.combat;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
@ -35,11 +34,13 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
}
/**
* Tests attacking creature doesn't untap after being blocked by Wall of Frost
* Tests attacking creature doesn't untap after being blocked by Wall of
* Frost
*/
@Test
public void testWallofWrost() {
addCard(Zone.BATTLEFIELD, playerA, "Wall of Frost");
// Whenever Wall of Frost blocks a creature, that creature doesn't untap during its controller's next untap step.
addCard(Zone.BATTLEFIELD, playerA, "Wall of Frost"); // 0/7
addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm");
attack(2, playerB, "Craw Wurm");
@ -56,9 +57,8 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
}
/**
* Tests card that says that it can't block specific cards
* Hunted Ghoul:
* Hunted Ghoul can't block Humans.
* Tests card that says that it can't block specific cards Hunted Ghoul:
* Hunted Ghoul can't block Humans.
*/
@Test
public void testFilteredBlocking() {
@ -76,9 +76,8 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
}
/**
* Tests card that says that it can't block specific cards still can block others
* Hunted Ghoul:
* Hunted Ghoul can't block Humans.
* Tests card that says that it can't block specific cards still can block
* others Hunted Ghoul: Hunted Ghoul can't block Humans.
*/
@Test
public void testFilteredBlocking2() {
@ -176,7 +175,8 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
}
/**
* Tests "Creatures with power less than Champion of Lambholt's power can't block creatures you control."
* Tests "Creatures with power less than Champion of Lambholt's power can't
* block creatures you control."
*/
@Test
public void testChampionOfLambholt() {
@ -244,22 +244,22 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
assertCounterCount(playerA, CounterType.POISON, 1);
}
@Test
public void testCantBeBlockedTormentedSoul() {
addCard(Zone.BATTLEFIELD, playerB, "Tormented Soul");
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
attack(2, playerB, "Tormented Soul");
block(2, playerA, "Tormented Soul", "Memnite");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Memnite", 1);
assertPermanentCount(playerB, "Tormented Soul", 1);
assertLife(playerA, 19);
}

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
@ -45,8 +47,10 @@ import mage.game.permanent.Permanent;
*/
public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousRuleModifyingEffectImpl {
private int validForTurnNum;
private UUID onlyIfControlledByPlayer;
private String targetName;
// holds the info what target was already handled in Untap of its controller
private final Map<UUID, Boolean> handledTargetsDuringTurn = new HashMap<>();
/**
* Attention: This effect won't work with targets controlled by different
@ -55,19 +59,30 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
*
*/
public DontUntapInControllersNextUntapStepTargetEffect() {
super(Duration.Custom, Outcome.Detriment, false, true);
this("");
}
public DontUntapInControllersNextUntapStepTargetEffect(String targetName) {
this();
this(targetName, null);
}
/**
*
* @param targetName used as target text for the generated rule text
* @param onlyIfControlledByPlayer the effect only works if the permanent is
* controlled by that controller, null = it works for all players
*/
public DontUntapInControllersNextUntapStepTargetEffect(String targetName, UUID onlyIfControlledByPlayer) {
super(Duration.Custom, Outcome.Detriment, false, true);
this.targetName = targetName;
this.onlyIfControlledByPlayer = onlyIfControlledByPlayer;
}
public DontUntapInControllersNextUntapStepTargetEffect(final DontUntapInControllersNextUntapStepTargetEffect effect) {
super(effect);
this.validForTurnNum = effect.validForTurnNum;
this.targetName = effect.targetName;
this.handledTargetsDuringTurn.putAll(effect.handledTargetsDuringTurn);
this.onlyIfControlledByPlayer = effect.onlyIfControlledByPlayer;
}
@Override
@ -97,42 +112,40 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// the check for turn number is needed if multiple effects are added to prevent untap in next untap step of controller
// if we don't check for turn number, every untap step of a turn only one effect would be used instead of correctly only one time
// to skip the untap effect.
// Discard effect if it's related to a previous turn
if (validForTurnNum > 0 && validForTurnNum < game.getTurnNum()) {
discard();
return false;
}
// remember the turn of the untap step the effect has to be applied
// the check if a permanent untap pahse is already handled is needed if multiple effects are added to prevent untap in next untap step of controller
// if we don't check it for every untap step of a turn only one effect would be consumed instead of all be valid for the next untap step
if (GameEvent.EventType.UNTAP_STEP.equals(event.getType())) {
UUID controllerId = null;
boolean allHandled = true;
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
controllerId = permanent.getControllerId();
if (game.getActivePlayerId().equals(permanent.getControllerId())
|| (game.getActivePlayerId().equals(onlyIfControlledByPlayer))) { // if effect works only for specific player, all permanents have to be set to handled in that players untap step
if (!handledTargetsDuringTurn.containsKey(targetId)) {
// it's the untep step of the current controller and the effect was not handled for this target yet, so do it now
handledTargetsDuringTurn.put(targetId, false);
allHandled = false;
} else if (!handledTargetsDuringTurn.get(targetId)) {
// if it was already ready to be handled on an previous Untap step set it to done if not already so
handledTargetsDuringTurn.put(targetId, true);
}
} else {
allHandled = false;
}
}
}
if (controllerId == null) { // no more targets on the battlefield, effect can be discarded
if (allHandled) {
discard();
return false;
}
if (game.getActivePlayerId().equals(controllerId)) {
if (validForTurnNum == game.getTurnNum()) { // the turn has a second untap step but the effect is already related to the first untap step
discard();
return false;
}
validForTurnNum = game.getTurnNum();
}
}
if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == EventType.UNTAP) {
if (targetPointer.getTargets(game, source).contains(event.getTargetId())) {
if (handledTargetsDuringTurn.containsKey(event.getTargetId())
&& !handledTargetsDuringTurn.get(event.getTargetId())
&& getTargetPointer().getTargets(game, source).contains(event.getTargetId())) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && game.getActivePlayerId().equals(permanent.getControllerId())) {
handledTargetsDuringTurn.put(event.getTargetId(), true);
return true;
}
}