mirror of
https://github.com/correl/mage.git
synced 2025-03-07 20:53:18 -10:00
* Exploit - Fixed that Exploit also triggered if the creature with Exploit left the battlefield before the first ability of Exploit resolved.
This commit is contained in:
parent
a56cccebf9
commit
f62d3ac227
17 changed files with 231 additions and 44 deletions
|
@ -1,7 +1,6 @@
|
|||
woogerworks (North America/USA) :xmage.woogerworks.com:17171
|
||||
Xmage.de 1 (Europe/Germany) :xmage.de:17171
|
||||
XMage.info 1 (Europe/France) :176.31.186.181:17171
|
||||
XMage.info 2 (Europe/France) :176.31.186.181:17000
|
||||
XMage.de 1 (Europe/Germany) fast :xmage.de:17171
|
||||
XMage.info 2 (Europe/France) slow :176.31.186.181:17000
|
||||
IceMage (Europe/Netherlands) :ring0.cc:17171
|
||||
Seedds Server (Asia) :115.29.203.80:17171
|
||||
localhost -> connect to your local server (must be started):localhost:17171
|
||||
|
|
|
@ -56,8 +56,6 @@ public class ScoutsWarning extends CardImpl {
|
|||
super(ownerId, 16, "Scout's Warning", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}");
|
||||
this.expansionSetCode = "FUT";
|
||||
|
||||
this.color.setWhite(true);
|
||||
|
||||
// The next creature card you play this turn can be played as though it had flash.
|
||||
this.getSpellAbility().addEffect(new ScoutsWarningAsThoughEffect());
|
||||
this.getSpellAbility().addWatcher(new ScoutsWarningWatcher());
|
||||
|
@ -117,7 +115,7 @@ class ScoutsWarningAsThoughEffect extends AsThoughEffectImpl {
|
|||
if (watcher.isScoutsWarningSpellActive(source.getSourceId(), zoneChangeCounter)) {
|
||||
Card card = game.getCard(sourceId);
|
||||
if (card != null && card.getCardType().contains(CardType.CREATURE) && source.getControllerId().equals(affectedControllerId)) {
|
||||
return card.getSpellAbility().isInUseableZone(game, card, false);
|
||||
return card.getSpellAbility().isInUseableZone(game, card, null);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -118,7 +118,7 @@ class QuickenAsThoughEffect extends AsThoughEffectImpl {
|
|||
if (quickenWatcher.isQuickenSpellActive(source.getSourceId(), zoneChangeCounter)) {
|
||||
Card card = game.getCard(sourceId);
|
||||
if (card != null && card.getCardType().contains(CardType.SORCERY) && source.getControllerId().equals(affectedControllerId)) {
|
||||
return card.getSpellAbility().isInUseableZone(game, card, false);
|
||||
return card.getSpellAbility().isInUseableZone(game, card, null);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -132,7 +132,7 @@ class SavageSummoningAsThoughEffect extends AsThoughEffectImpl {
|
|||
if (watcher.isSavageSummoningSpellActive()) {
|
||||
Card card = game.getCard(sourceId);
|
||||
if (card != null && card.getCardType().contains(CardType.CREATURE) && card.getOwnerId().equals(source.getControllerId())) {
|
||||
return card.getSpellAbility().isInUseableZone(game, card, false);
|
||||
return card.getSpellAbility().isInUseableZone(game, card, null);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* 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 org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class ExploitTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* 702.109. Exploit
|
||||
*
|
||||
* 702.109a Exploit is a triggered ability. “Exploit” means “When this creature enters the battlefield, you may sacrifice a creature.”
|
||||
*
|
||||
* 702.109b A creature with exploit “exploits a creature” when the controller of the exploit ability sacrifices a creature as that ability resolves.
|
||||
*
|
||||
* You choose whether to sacrifice a creature and which creature to sacrifice as the exploit ability resolves.
|
||||
* You can sacrifice the creature with exploit if it’s still on the battlefield. This will cause its other ability to trigger.
|
||||
* You can’t sacrifice more than one creature to any one exploit ability.
|
||||
*
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testNormalUse() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
// Exploit (When this creature enters the battlefield, you may sacrifice a creature.)
|
||||
// When Silumgar Butcher exploits a creature, target creature gets -3/-3 until end of turn.
|
||||
addCard(Zone.HAND, playerA, "Silumgar Butcher");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Thundering Giant"); // 4/3
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silumgar Butcher");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, "Silvercoat Lion"); // sacrifice to Exploit
|
||||
addTarget(playerA, "Thundering Giant"); // Target for the -3/-3
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Silumgar Butcher", 1);
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
assertGraveyardCount(playerB, "Thundering Giant", 1);
|
||||
|
||||
}
|
||||
/**
|
||||
* Test that the Exploit ability won't trigger if the creature with
|
||||
* exploit left the battlefiled before the Enters the battlefield
|
||||
* triggered ability resolves.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testExploitTriggerWontGo() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
// Exploit (When this creature enters the battlefield, you may sacrifice a creature.)
|
||||
// When Silumgar Butcher exploits a creature, target creature gets -3/-3 until end of turn.
|
||||
addCard(Zone.HAND, playerA, "Silumgar Butcher");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Thundering Giant"); // 4/3
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silumgar Butcher");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Silumgar Butcher");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, "Silvercoat Lion"); // sacrifice to Exploit
|
||||
addTarget(playerA, "Thundering Giant"); // Target for the -3/-3
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||
assertGraveyardCount(playerA, "Silumgar Butcher", 1);
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
assertPermanentCount(playerB, "Thundering Giant", 1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the Exploit ability won't trigger if the creature with
|
||||
* exploit left the battlefiled before the Enters the battlefield
|
||||
* triggered ability resolves.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testSacrificeCreatureWithExploit() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
// Exploit (When this creature enters the battlefield, you may sacrifice a creature.)
|
||||
// When Qarsi Sadist exploits a creature, target opponent loses 2 life and you gain 2 life.
|
||||
addCard(Zone.HAND, playerA, "Qarsi Sadist");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Qarsi Sadist");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, "Qarsi Sadist"); // sacrifice to Exploit
|
||||
addTarget(playerA, playerB); // Target for lose life
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Qarsi Sadist", 1);
|
||||
|
||||
assertLife(playerA, 22);
|
||||
assertLife(playerB, 18);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -47,6 +47,7 @@ import mage.constants.EffectType;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.Targets;
|
||||
|
@ -382,10 +383,10 @@ public interface Ability extends Controllable, Serializable {
|
|||
*
|
||||
* @param game
|
||||
* @param source
|
||||
* @param checkLKI
|
||||
* @param event
|
||||
* @return
|
||||
*/
|
||||
boolean isInUseableZone(Game game, MageObject source, boolean checkLKI);
|
||||
boolean isInUseableZone(Game game, MageObject source, GameEvent event);
|
||||
|
||||
/**
|
||||
* Returns true if this ability has to be shown as topmost of all the rules of the object
|
||||
|
|
|
@ -67,6 +67,7 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ManaEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentCard;
|
||||
|
@ -864,11 +865,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
*
|
||||
* @param game
|
||||
* @param source
|
||||
* @param checkShortLivingLKI if the object was in the needed zone as the effect that's currently applied started, the check returns true
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, boolean checkShortLivingLKI) {
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
if (zone.equals(Zone.COMMAND)) {
|
||||
if (this.getSourceId() == null) { // commander effects
|
||||
return true;
|
||||
|
@ -880,17 +880,6 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
|
||||
// try LKI first (was the object with the id in the needed zone before)
|
||||
if (checkShortLivingLKI) {
|
||||
if (game.getShortLivingLKI(getSourceId(), zone)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (game.getLastKnownInformation(getSourceId(), zone) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MageObject object;
|
||||
UUID parameterSourceId;
|
||||
// for singleton abilities like Flying we can't rely on abilities' source because it's only once in continuous effects
|
||||
|
|
|
@ -28,9 +28,13 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -49,6 +53,14 @@ public abstract class StaticAbility extends AbilityImpl {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
if (game.getShortLivingLKI(getSourceId(), zone)) {
|
||||
return true;
|
||||
}
|
||||
return super.isInUseableZone(game, source, event);
|
||||
}
|
||||
|
||||
public StaticAbility(StaticAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbili
|
|||
}
|
||||
// for effects like when leaves battlefield or destroyed use ShortLKI to check if permanent was in the correct zone before (e.g. Oblivion Ring or Karmic Justice)
|
||||
MageObject object = game.getObject(ability.getSourceId());
|
||||
if (ability.isInUseableZone(game, object, false)) {
|
||||
if (ability.isInUseableZone(game, object, event)) {
|
||||
if (!game.getContinuousEffects().preventedByRuleModification(event, ability, game, false)) {
|
||||
if (object != null) {
|
||||
boolean controllerSet = false;
|
||||
|
|
|
@ -35,6 +35,7 @@ import mage.constants.AbilityType;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
|
@ -158,4 +159,36 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
/**
|
||||
* 603.6. Trigger events that involve objects changing zones are called “zone-change triggers.”
|
||||
* Many abilities with zone-change triggers attempt to do something to that object after it
|
||||
* changes zones. During resolution, these abilities look for the object in the zone that
|
||||
* it moved to. If the object is unable to be found in the zone it went to, the part of the
|
||||
* ability attempting to do something to the object will fail to do anything. The ability could
|
||||
* be unable to find the object because the object never entered the specified zone, because it
|
||||
* left the zone before the ability resolved, or because it is in a zone that is hidden from
|
||||
* a player, such as a library or an opponent’s hand. (This rule applies even if the object
|
||||
* leaves the zone and returns again before the ability resolves.) The most common zone-change
|
||||
* triggers are enters-the-battlefield triggers and leaves-the-battlefield triggers.
|
||||
*/
|
||||
if (event != null) {
|
||||
switch (event.getType()) {
|
||||
case ZONE_CHANGE:
|
||||
if (source == null && ((ZoneChangeEvent)event).getTarget() != null) {
|
||||
source = ((ZoneChangeEvent)event).getTarget();
|
||||
}
|
||||
case DESTROYED_PERMANENT:
|
||||
// case LOST_CONTROL:
|
||||
case PHASED_OUT:
|
||||
case PHASED_IN:
|
||||
if (game.getLastKnownInformation(getSourceId(), zone) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.isInUseableZone(game, source, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import mage.abilities.keyword.CyclingAbility;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
/**
|
||||
|
@ -55,7 +56,7 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, boolean checkLKI) {
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ public class DiesTriggeredAbility extends ZoneChangeTriggeredAbility {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, boolean checkLKI) {
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
// check it was previously on battlefield
|
||||
Permanent before = (Permanent) game.getLastKnownInformation(sourceId, Zone.BATTLEFIELD);
|
||||
// check now it is in graveyard
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
|
@ -48,7 +49,7 @@ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
public ExploitCreatureTriggeredAbility(Effect effect, boolean optional, SetTargetPointer setTargetPointer) {
|
||||
super(Zone.ALL, effect, optional);
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
}
|
||||
|
||||
|
@ -62,11 +63,20 @@ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return new ExploitCreatureTriggeredAbility(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.EXPLOITED_CREATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
if (event.getTargetId().equals(getSourceId()) && event.getSourceId().equals(getSourceId())) {
|
||||
return true; // if Exploits creature sacrifices itself, exploit triggers
|
||||
}
|
||||
return super.isInUseableZone(game, source, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getSourceId().equals(getSourceId())) {
|
||||
|
|
|
@ -214,7 +214,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
||||
for (Ability ability: abilities) {
|
||||
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, true)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
|
||||
layerEffects.add(effect);
|
||||
break;
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> abilities = requirementEffects.getAbility(effect.getId());
|
||||
HashSet<Ability> applicableAbilities = new HashSet<>();
|
||||
for (Ability ability : abilities) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, ability instanceof MageSingleton ? permanent : null, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, ability instanceof MageSingleton ? permanent : null, null)) {
|
||||
if (effect.applies(permanent, ability, game)) {
|
||||
applicableAbilities.add(ability);
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> abilities = restrictionEffects.getAbility(effect.getId());
|
||||
HashSet<Ability> applicableAbilities = new HashSet<>();
|
||||
for (Ability ability : abilities) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, ability instanceof MageSingleton ? permanent : null, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, ability instanceof MageSingleton ? permanent : null, null)) {
|
||||
if (effect.applies(permanent, ability, game)) {
|
||||
applicableAbilities.add(ability);
|
||||
}
|
||||
|
@ -306,7 +306,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> abilities = restrictionUntapNotMoreThanEffects.getAbility(effect.getId());
|
||||
HashSet<Ability> applicableAbilities = new HashSet<>();
|
||||
for (Ability ability : abilities) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
|
||||
if (effect.applies(player, ability, game)) {
|
||||
applicableAbilities.add(ability);
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> applicableAbilities = new HashSet<>();
|
||||
for (Ability ability : abilities) {
|
||||
// for replacment effects of static abilities do not use LKI to check if to apply
|
||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, true)) {
|
||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
|
||||
if (checkAbilityStillExists(ability, effect, event, game)) { // TODO: This is really needed???
|
||||
|
@ -376,7 +376,7 @@ public class ContinuousEffects implements Serializable {
|
|||
HashSet<Ability> abilities = preventionEffects.getAbility(effect.getId());
|
||||
HashSet<Ability> applicableAbilities = new HashSet<>();
|
||||
for (Ability ability : abilities) {
|
||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, true)) {
|
||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
if (effect.applies(event, ability, game)) {
|
||||
applicableAbilities.add(ability);
|
||||
|
@ -443,7 +443,7 @@ public class ContinuousEffects implements Serializable {
|
|||
for (CostModificationEffect effect: costModificationEffects) {
|
||||
HashSet<Ability> abilities = costModificationEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
costEffects.add(effect);
|
||||
break;
|
||||
|
@ -466,7 +466,7 @@ public class ContinuousEffects implements Serializable {
|
|||
for (SpliceCardEffect effect: spliceCardEffects) {
|
||||
HashSet<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (ability.getControllerId().equals(playerId) && (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false))) {
|
||||
if (ability.getControllerId().equals(playerId) && (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null))) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
spliceEffects.add(effect);
|
||||
break;
|
||||
|
@ -516,7 +516,7 @@ public class ContinuousEffects implements Serializable {
|
|||
for (AsThoughEffect effect: asThoughEffectsMap.get(type)) {
|
||||
HashSet<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
asThoughEffectsList.add(effect);
|
||||
break;
|
||||
|
@ -660,7 +660,7 @@ public class ContinuousEffects implements Serializable {
|
|||
continue;
|
||||
}
|
||||
for (Ability sourceAbility : continuousRuleModifyingEffects.getAbility(effect.getId())) {
|
||||
if (!(sourceAbility instanceof StaticAbility) || sourceAbility.isInUseableZone(game, null, true)) {
|
||||
if (!(sourceAbility instanceof StaticAbility) || sourceAbility.isInUseableZone(game, null, event)) {
|
||||
if (checkAbilityStillExists(sourceAbility, effect, event, game)) {
|
||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
effect.setValue("targetAbility", targetAbility);
|
||||
|
|
|
@ -40,6 +40,7 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
@ -158,7 +159,7 @@ class HauntExileAbility extends ZoneChangeTriggeredAbility {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, boolean checkLKI) {
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
boolean fromOK = true;
|
||||
if (creatureHaunt) {
|
||||
// check it was previously on battlefield
|
||||
|
@ -209,8 +210,9 @@ class HauntEffect extends OneShotEffect {
|
|||
game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source)));
|
||||
card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game);
|
||||
hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game);
|
||||
if (!game.isSimulation())
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(new StringBuilder(card.getName()).append(" haunting ").append(hauntedCreature.getLogName()).toString());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AbilityWord;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
|
@ -100,8 +101,9 @@ public class StackAbility implements StackObject, Ability {
|
|||
if (ability.getTargets().stillLegal(ability, game)) {
|
||||
return ability.resolve(game);
|
||||
}
|
||||
if (!game.isSimulation())
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("Ability has been fizzled: " + getRule());
|
||||
}
|
||||
counter(null, game);
|
||||
return false;
|
||||
}
|
||||
|
@ -400,7 +402,7 @@ public class StackAbility implements StackObject, Ability {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, boolean checkLKI) {
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
|
|
|
@ -189,10 +189,10 @@ public class TraceUtil {
|
|||
for (RestrictionEffect effect: restrictionEffects) {
|
||||
log.error(uuid+" effect=" + effect.toString() + " id=" + effect.getId());
|
||||
for (Ability ability : restrictionEffects.getAbility(effect.getId())) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, permanent, false)) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, permanent, null)) {
|
||||
log.error(uuid+" ability=" + ability + ", applies_to_attacker=" + effect.applies(permanent, ability, game));
|
||||
} else {
|
||||
boolean usable = ability.isInUseableZone(game, permanent, false);
|
||||
boolean usable = ability.isInUseableZone(game, permanent, null);
|
||||
log.error(uuid+" instanceof StaticAbility: " + (ability instanceof StaticAbility) + ", ability=" + ability);
|
||||
log.error(uuid+" usable zone: " + usable + ", ability=" + ability);
|
||||
if (!usable) {
|
||||
|
|
Loading…
Add table
Reference in a new issue