mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
CantExile: add checks for Effects and Costs (#4632)
This commit is contained in:
@@ -59,7 +59,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public PaymentDecision visit(CostCollectEvidence cost) {
|
||||
int c = cost.getAbilityAmount(ability);
|
||||
CardCollectionView chosen = ComputerUtil.chooseCollectEvidence(player, cost, source, c, ability);
|
||||
CardCollectionView chosen = ComputerUtil.chooseCollectEvidence(player, cost, source, c, ability, isEffect());
|
||||
|
||||
return null == chosen ? null : PaymentDecision.card(chosen);
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
// TODO Determine exile from same zone for AI
|
||||
return null;
|
||||
} else {
|
||||
CardCollectionView chosen = ComputerUtil.chooseExileFrom(player, cost, source, c, ability);
|
||||
CardCollectionView chosen = ComputerUtil.chooseExileFrom(player, cost, source, c, ability, isEffect());
|
||||
return null == chosen ? null : PaymentDecision.card(chosen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,8 +665,8 @@ public class ComputerUtil {
|
||||
return sacList;
|
||||
}
|
||||
|
||||
public static CardCollection chooseCollectEvidence(final Player ai, CostCollectEvidence cost, final Card activate, int amount, SpellAbility sa) {
|
||||
CardCollection typeList = new CardCollection(ai.getCardsIn(ZoneType.Graveyard));
|
||||
public static CardCollection chooseCollectEvidence(final Player ai, CostCollectEvidence cost, final Card activate, int amount, SpellAbility sa, final boolean effect) {
|
||||
CardCollection typeList = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.canExiledBy(sa, effect));
|
||||
|
||||
if (CardLists.getTotalCMC(typeList) < amount) return null;
|
||||
|
||||
@@ -692,7 +692,7 @@ public class ComputerUtil {
|
||||
return exileList;
|
||||
}
|
||||
|
||||
public static CardCollection chooseExileFrom(final Player ai, CostExile cost, final Card activate, final int amount, SpellAbility sa) {
|
||||
public static CardCollection chooseExileFrom(final Player ai, CostExile cost, final Card activate, final int amount, SpellAbility sa, final boolean effect) {
|
||||
CardCollection typeList;
|
||||
if (cost.zoneRestriction != 1) {
|
||||
typeList = new CardCollection(ai.getGame().getCardsIn(cost.from));
|
||||
@@ -700,6 +700,7 @@ public class ComputerUtil {
|
||||
typeList = new CardCollection(ai.getCardsIn(cost.from));
|
||||
}
|
||||
typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), activate.getController(), activate, sa);
|
||||
typeList = CardLists.filter(typeList, CardPredicates.canExiledBy(sa, effect));
|
||||
|
||||
// don't exile the card we're pumping
|
||||
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
int amt = part.getAbilityAmount(sa);
|
||||
needed += amt;
|
||||
CardCollection toAdd = ComputerUtil.chooseExileFrom(ai, (CostExile) part, source, amt, sa);
|
||||
CardCollection toAdd = ComputerUtil.chooseExileFrom(ai, (CostExile) part, source, amt, sa, true);
|
||||
if (toAdd != null) {
|
||||
payingCards.addAll(toAdd);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.google.common.eventbus.EventBus;
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardType.Supertype;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
@@ -950,36 +949,6 @@ public class Game {
|
||||
activePlanes = activePlane0;
|
||||
}
|
||||
|
||||
public void archenemy904_10() {
|
||||
//904.10. If a non-ongoing scheme card is face up in the
|
||||
//command zone, and it isn't the source of a triggered ability
|
||||
//that has triggered but not yet left the stack, that scheme card
|
||||
//is turned face down and put on the bottom of its owner's scheme
|
||||
//deck the next time a player would receive priority.
|
||||
//(This is a state-based action. See rule 704.)
|
||||
|
||||
for (int i = 0; i < getCardsIn(ZoneType.Command).size(); i++) {
|
||||
Card c = getCardsIn(ZoneType.Command).get(i);
|
||||
if (c.isScheme() && !c.getType().hasSupertype(Supertype.Ongoing)) {
|
||||
boolean foundonstack = false;
|
||||
for (SpellAbilityStackInstance si : stack) {
|
||||
if (si.getSourceCard().equals(c)) {
|
||||
foundonstack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundonstack) {
|
||||
getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
c.getController().getZone(ZoneType.Command).remove(c);
|
||||
i--;
|
||||
getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
c.getController().getZone(ZoneType.SchemeDeck).add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameStage getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import forge.GameCommand;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.CardType.Supertype;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.ability.*;
|
||||
import forge.game.card.*;
|
||||
@@ -747,16 +748,11 @@ public class GameAction {
|
||||
// use FThreads.invokeInNewThread to run code in a pooled thread
|
||||
return moveTo(zoneTo, c, null, cause, params);
|
||||
}
|
||||
public final Card moveTo(final Zone zoneTo, Card c, Integer position, SpellAbility cause) {
|
||||
return moveTo(zoneTo, c, position, cause, AbilityKey.newMap());
|
||||
}
|
||||
|
||||
public final Card moveTo(final ZoneType name, final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
return moveTo(name, c, 0, cause, params);
|
||||
}
|
||||
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) {
|
||||
return moveTo(name, c, libPosition, cause, AbilityKey.newMap());
|
||||
}
|
||||
|
||||
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
// Call specific functions to set PlayerZone, then move onto moveTo
|
||||
switch(name) {
|
||||
@@ -764,7 +760,11 @@ public class GameAction {
|
||||
case Library: return moveToLibrary(c, libPosition, cause, params);
|
||||
case Battlefield: return moveToPlay(c, c.getController(), cause, params);
|
||||
case Graveyard: return moveToGraveyard(c, cause, params);
|
||||
case Exile: return exile(c, cause, params);
|
||||
case Exile:
|
||||
if (!c.canExiledBy(cause, true)) {
|
||||
return null;
|
||||
}
|
||||
return exile(c, cause, params);
|
||||
case Stack: return moveToStack(c, cause, params);
|
||||
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
|
||||
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
|
||||
@@ -943,6 +943,20 @@ public class GameAction {
|
||||
return copied;
|
||||
}
|
||||
|
||||
public final Card exileEffect(final Card effect) {
|
||||
return exile(effect, null, null);
|
||||
}
|
||||
|
||||
public final void moveToCommand(final Card effect, final SpellAbility sa) {
|
||||
moveToCommand(effect, sa, AbilityKey.newMap());
|
||||
}
|
||||
public final void moveToCommand(final Card effect, final SpellAbility sa, Map<AbilityKey, Object> params) {
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
moveTo(ZoneType.Command, effect, sa, params);
|
||||
effect.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
|
||||
public void ceaseToExist(Card c, boolean skipTrig) {
|
||||
if (c.isInZone(ZoneType.Stack)) {
|
||||
c.getGame().getStack().remove(c);
|
||||
@@ -1226,12 +1240,6 @@ public class GameAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Max: I don't know where to put this! - but since it's a state based action, it must be in check state effects
|
||||
if (game.getRules().hasAppliedVariant(GameType.Archenemy)
|
||||
|| game.getRules().hasAppliedVariant(GameType.ArchenemyRumble)) {
|
||||
game.archenemy904_10();
|
||||
}
|
||||
|
||||
final boolean refreeze = game.getStack().isFrozen();
|
||||
game.getStack().setFrozen(true);
|
||||
game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects
|
||||
@@ -1266,6 +1274,7 @@ public class GameAction {
|
||||
checkAgain |= stateBasedAction704_5d(c);
|
||||
// Dungeon Card won't affect other cards, so don't need to set checkAgain
|
||||
stateBasedAction_Dungeon(c);
|
||||
stateBasedAction_Scheme(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1545,6 +1554,15 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
private void stateBasedAction_Scheme(Card c) {
|
||||
if (!c.isScheme() || c.getType().hasSupertype(Supertype.Ongoing)) {
|
||||
return;
|
||||
}
|
||||
if (!game.getStack().hasSourceOnStack(c, null)) {
|
||||
moveTo(ZoneType.SchemeDeck, c, null, AbilityKey.newMap());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stateBasedAction704_attach(Card c, CardCollection unAttachList) {
|
||||
boolean checkAgain = false;
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.staticability.StaticAbilityLayer;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
@@ -757,10 +756,7 @@ public final class GameActionUtil {
|
||||
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
|
||||
return eff;
|
||||
}
|
||||
|
||||
@@ -536,11 +536,7 @@ public abstract class SpellAbilityEffect {
|
||||
eff.copyChangedTextFrom(card);
|
||||
}
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, null);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
|
||||
protected static void addLeaveBattlefieldReplacement(final Card eff, final String zone) {
|
||||
@@ -648,22 +644,9 @@ public abstract class SpellAbilityEffect {
|
||||
eff.copyChangedTextFrom(host);
|
||||
}
|
||||
|
||||
final GameCommand endEffect = new GameCommand() {
|
||||
private static final long serialVersionUID = -5861759814760561373L;
|
||||
game.getEndOfTurn().addUntil(exileEffectCommand(game, eff));
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
game.getEndOfTurn().addUntil(endEffect);
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, null);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,4 +974,15 @@ public abstract class SpellAbilityEffect {
|
||||
}
|
||||
return new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||
}
|
||||
|
||||
public static GameCommand exileEffectCommand(final Game game, final Card effect) {
|
||||
return new GameCommand() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exileEffect(effect);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game.ability.effects;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -11,8 +10,6 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
@@ -79,10 +76,7 @@ public class AddTurnEffect extends SpellAbilityEffect {
|
||||
|
||||
eff.addStaticAbility(stEffect);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -197,6 +197,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
||||
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa, moveParams);
|
||||
} else {
|
||||
if (destination == ZoneType.Exile && !c.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa, moveParams);
|
||||
if (destination == ZoneType.Exile) {
|
||||
handleExiledWith(movedCard, sa);
|
||||
|
||||
@@ -702,6 +702,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
} else {
|
||||
// might set before card is moved only for nontoken
|
||||
if (destination.equals(ZoneType.Exile)) {
|
||||
if (!gameCard.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
handleExiledWith(gameCard, sa);
|
||||
}
|
||||
|
||||
@@ -1384,6 +1387,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
else if (destination.equals(ZoneType.Exile)) {
|
||||
if (!c.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
movedCard = game.getAction().exile(c, sa, moveParams);
|
||||
|
||||
handleExiledWith(movedCard, sa);
|
||||
@@ -1553,6 +1559,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
} else if (srcSA.getParam("Destination").equals("Graveyard")) {
|
||||
movedCard = game.getAction().moveToGraveyard(tgtHost, srcSA, params);
|
||||
} else if (srcSA.getParam("Destination").equals("Exile")) {
|
||||
if (!tgtHost.canExiledBy(srcSA, true)) {
|
||||
return;
|
||||
}
|
||||
movedCard = game.getAction().exile(tgtHost, srcSA, params);
|
||||
handleExiledWith(movedCard, srcSA);
|
||||
} else if (srcSA.getParam("Destination").equals("TopOfLibrary")) {
|
||||
|
||||
@@ -265,6 +265,9 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
} else if (destination.equals("Graveyard")) {
|
||||
movedCard = game.getAction().moveToGraveyard(c, srcSA, params);
|
||||
} else if (destination.equals("Exile")) {
|
||||
if (!c.canExiledBy(srcSA, true)) {
|
||||
return false;
|
||||
}
|
||||
movedCard = game.getAction().exile(c, srcSA, params);
|
||||
} else if (destination.equals("Hand")) {
|
||||
movedCard = game.getAction().moveToHand(c, srcSA, params);
|
||||
|
||||
@@ -6,7 +6,6 @@ import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -16,8 +15,6 @@ import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
||||
@@ -66,10 +63,7 @@ public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
||||
addForgetOnMovedTrigger(eff, "Battlefield");
|
||||
}
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
|
||||
o.getView().updatePreventNextDamage(o);
|
||||
if (o instanceof Player) {
|
||||
@@ -81,7 +75,7 @@ public abstract class DamagePreventEffectBase extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
game.getAction().exileEffect(eff);
|
||||
o.getView().updatePreventNextDamage(o);
|
||||
if (o instanceof Player) {
|
||||
game.fireEvent(new GameEventPlayerStatsChanged((Player) o, false));
|
||||
|
||||
@@ -379,8 +379,12 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
|
||||
for (Card c : movedCards) {
|
||||
if (destZone1.equals(ZoneType.Library) || destZone1.equals(ZoneType.PlanarDeck) || destZone1.equals(ZoneType.SchemeDeck)) {
|
||||
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa);
|
||||
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa, AbilityKey.newMap());
|
||||
} else {
|
||||
if (destZone1.equals(ZoneType.Exile) && !c.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.hasParam("Tapped")) {
|
||||
c.setTapped(true);
|
||||
}
|
||||
@@ -470,6 +474,9 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
} else {
|
||||
// just move them randomly
|
||||
for (Card c : rest) {
|
||||
if (destZone2 == ZoneType.Exile && !c.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
c = game.getAction().moveTo(destZone2, c, sa, moveParams);
|
||||
if (destZone2 == ZoneType.Exile) {
|
||||
if (sa.hasParam("ExileWithCounter")) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -120,7 +121,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
||||
|
||||
if (!sa.hasParam("ChangeLater")) {
|
||||
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
|
||||
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa);
|
||||
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa, AbilityKey.newMap());
|
||||
} else {
|
||||
if (destZone1.equals(ZoneType.Battlefield)) {
|
||||
if (sa.hasParam("Tapped")) {
|
||||
@@ -164,7 +165,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
||||
}
|
||||
for (final Card c : afterOrder) {
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
Card m = game.getAction().moveTo(destZone2, c, libraryPosition2, sa);
|
||||
Card m = game.getAction().moveTo(destZone2, c, libraryPosition2, sa, AbilityKey.newMap());
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ import java.util.*;
|
||||
CardCollection drafted = new CardCollection();
|
||||
|
||||
for (int i = 0; i < numToDraft; i++) {
|
||||
String chosen = "";
|
||||
Collections.shuffle(spellbook);
|
||||
List<Card> draftOptions = new ArrayList<>();
|
||||
for (String name : spellbook.subList(0, 3)) {
|
||||
@@ -69,6 +68,10 @@ import java.util.*;
|
||||
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
for (final Card c : drafted) {
|
||||
if (zone.equals(ZoneType.Exile) && !c.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Card made = game.getAction().moveTo(zone, c, sa, moveParams);
|
||||
if (zone.equals(ZoneType.Exile)) {
|
||||
handleExiledWith(made, sa);
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.Map;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.ImageKeys;
|
||||
import forge.card.CardRarity;
|
||||
import forge.game.Game;
|
||||
@@ -26,7 +25,6 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollection;
|
||||
@@ -307,30 +305,14 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (duration == null || !duration.equals("Permanent")) {
|
||||
final GameCommand endEffect = new GameCommand() {
|
||||
private static final long serialVersionUID = -5861759814760561373L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
addUntilCommand(sa, endEffect, controller);
|
||||
addUntilCommand(sa, exileEffectCommand(game, eff), controller);
|
||||
}
|
||||
|
||||
if (sa.hasParam("ImprintOnHost")) {
|
||||
hostCard.addImprintedCard(eff);
|
||||
}
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, params);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
//if (effectTriggers != null) {
|
||||
// game.getTriggerHandler().registerActiveTrigger(cmdEffect, false);
|
||||
//}
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class FogEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -32,18 +28,8 @@ public class FogEffect extends SpellAbilityEffect {
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
|
||||
game.getEndOfTurn().addUntil(new GameCommand() {
|
||||
private static final long serialVersionUID = -3297629217432253089L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
});
|
||||
game.getEndOfTurn().addUntil(exileEffectCommand(game, eff));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class MeldEffect extends SpellAbilityEffect {
|
||||
|
||||
Card secondary = controller.getController().chooseSingleEntityForEffect(field, sa, Localizer.getInstance().getMessage("lblChooseCardToMeld"), null);
|
||||
|
||||
CardCollection exiled = new CardCollection(Arrays.asList(hostCard, secondary));
|
||||
CardCollection exiled = CardLists.filter(Arrays.asList(hostCard, secondary), CardPredicates.canExiledBy(sa, true));
|
||||
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
CardZoneTable table = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard());
|
||||
@@ -48,8 +48,12 @@ public class MeldEffect extends SpellAbilityEffect {
|
||||
exiled = game.getAction().exile(exiled, sa, moveParams);
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
|
||||
Card primary = exiled.get(0);
|
||||
secondary = exiled.get(1);
|
||||
if (exiled.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Card primary = exiled.get(hostCard);
|
||||
secondary = exiled.get(secondary);
|
||||
|
||||
// cards has wrong name in exile
|
||||
if (!primary.sharesNameWith(primName) || !secondary.sharesNameWith(secName)) {
|
||||
|
||||
@@ -80,7 +80,7 @@ public class MutateEffect extends SpellAbilityEffect {
|
||||
game.getTriggerHandler().clearActiveTriggers(target, null);
|
||||
game.getTriggerHandler().registerActiveTrigger(target, false);
|
||||
|
||||
game.getAction().moveTo(p.getZone(ZoneType.Merged), host, sa);
|
||||
game.getAction().moveTo(p.getZone(ZoneType.Merged), host, sa, AbilityKey.newMap());
|
||||
|
||||
host.setTapped(target.isTapped());
|
||||
host.setFlipped(target.isFlipped());
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.game.Game;
|
||||
@@ -42,7 +41,6 @@ import forge.game.spellability.AlternativeCost;
|
||||
import forge.game.spellability.LandAbility;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
@@ -521,24 +519,11 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
eff.copyChangedTextFrom(hostCard);
|
||||
}
|
||||
|
||||
final GameCommand endEffect = new GameCommand() {
|
||||
private static final long serialVersionUID = -5861759814760561373L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
game.getEndOfTurn().addUntil(endEffect);
|
||||
game.getEndOfTurn().addUntil(exileEffectCommand(game, eff));
|
||||
|
||||
tgtSA.addRollbackEffect(eff);
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, moveParams);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
|
||||
protected void addIllusionaryMaskReplace(Card c, SpellAbility sa, Map<AbilityKey, Object> moveParams) {
|
||||
@@ -572,9 +557,6 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
addExileOnMovedTrigger(eff, "Battlefield");
|
||||
addExileOnCounteredTrigger(eff);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, moveParams);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ package forge.game.ability.effects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -13,8 +11,6 @@ import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -67,19 +63,8 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
|
||||
for (final Card c : list) {
|
||||
c.incShieldCount();
|
||||
}
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
|
||||
final GameCommand untilEOT = new GameCommand() {
|
||||
private static final long serialVersionUID = 259368227093961103L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
};
|
||||
game.getEndOfTurn().addUntil(untilEOT);
|
||||
game.getEndOfTurn().addUntil(exileEffectCommand(game, eff));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
|
||||
|
||||
if (!StringUtils.isNumeric(varValue) && card.getSVar(varValue).startsWith("Number$")) {
|
||||
if (card.isImmutable() && prevent <= 0) {
|
||||
game.getAction().exile(card, null, null);
|
||||
game.getAction().exileEffect(card);
|
||||
} else {
|
||||
card.setSVar(varValue, "Number$" + prevent);
|
||||
card.updateAbilityTextForView();
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
|
||||
prevent -= n;
|
||||
|
||||
if (card.isImmutable() && prevent <= 0) {
|
||||
game.getAction().exile(card, null, null);
|
||||
game.getAction().exileEffect(card);
|
||||
} else if (!StringUtils.isNumeric(varValue)) {
|
||||
sa.setSVar(varValue, "Number$" + prevent);
|
||||
card.updateAbilityTextForView();
|
||||
|
||||
@@ -12,7 +12,6 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbilityCantSetSchemesInMotion;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class SetInMotionEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -40,16 +39,14 @@ public class SetInMotionEffect extends SpellAbilityEffect {
|
||||
return;
|
||||
}
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, controller.getActiveScheme(), null, AbilityKey.newMap());
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(controller.getActiveScheme(), sa);
|
||||
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.Scheme, controller.getActiveScheme());
|
||||
game.getTriggerHandler().runTrigger(TriggerType.SetInMotion, runParams, false);
|
||||
} else {
|
||||
controller.setSchemeInMotion();
|
||||
controller.setSchemeInMotion(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.List;
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
@@ -13,8 +12,6 @@ import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class SkipPhaseEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -102,16 +99,7 @@ public class SkipPhaseEffect extends SpellAbilityEffect {
|
||||
re.setOverridingAbility(exile);
|
||||
}
|
||||
if (duration != null) {
|
||||
final GameCommand endEffect = new GameCommand() {
|
||||
private static final long serialVersionUID = -5861759814760561373L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getAction().exile(eff, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
addUntilCommand(sa, endEffect);
|
||||
addUntilCommand(sa, exileEffectCommand(game, eff));
|
||||
}
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
@@ -121,18 +109,12 @@ public class SkipPhaseEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
};
|
||||
game.getUpkeep().addUntil(player, startEffect);
|
||||
} else {
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.util.List;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -14,8 +13,6 @@ import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
|
||||
public class SkipTurnEffect extends SpellAbilityEffect {
|
||||
@@ -61,10 +58,7 @@ public class SkipTurnEffect extends SpellAbilityEffect {
|
||||
re.setOverridingAbility(calcTurn);
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap());
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,7 @@ public class VentureEffect extends SpellAbilityEffect {
|
||||
String message = Localizer.getInstance().getMessage("lblChooseDungeon");
|
||||
Card dungeon = player.getController().chooseDungeon(player, dungeonCards, message);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, dungeon, sa, moveParams);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(dungeon, sa, moveParams);
|
||||
|
||||
return dungeon;
|
||||
}
|
||||
|
||||
@@ -7058,6 +7058,18 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return !StaticAbilityCantSacrifice.cantSacrifice(this, source, effect);
|
||||
}
|
||||
|
||||
public final boolean canExiledBy(final SpellAbility source, final boolean effect) {
|
||||
final Card gameCard = game.getCardState(this, null);
|
||||
// gameCard is LKI in that case, the card is not in game anymore
|
||||
// or the timestamp did change
|
||||
// this should check Self too
|
||||
if (gameCard == null || !this.equalsWithTimestamp(gameCard)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !StaticAbilityCantExile.cantExile(this, source, effect);
|
||||
}
|
||||
|
||||
public CardRules getRules() {
|
||||
return cardRules;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,15 @@ public final class CardPredicates {
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> canExiledBy(final SpellAbility sa, final boolean effect) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.canExiledBy(sa, effect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> canBeAttached(final Card aura, final SpellAbility sa) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -47,7 +48,7 @@ public class CostCollectEvidence extends CostPartWithList {
|
||||
|
||||
// This may need to be updated if we get a card like "Cards in graveyards can't be exiled to pay for costs"
|
||||
|
||||
return CardLists.getTotalCMC(payer.getCardsIn(ZoneType.Graveyard)) >= amount;
|
||||
return CardLists.getTotalCMC(CardLists.filter(payer.getCardsIn(ZoneType.Graveyard), CardPredicates.canExiledBy(ability, effect))) >= amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -175,8 +175,8 @@ public class CostExile extends CostPartWithList {
|
||||
type = TextUtil.fastReplace(type, "FromTopGrave", "");
|
||||
}
|
||||
|
||||
CardCollection list = new CardCollection(zoneRestriction != 1 ? game.getCardsIn(this.from) :
|
||||
payer.getCardsIn(this.from));
|
||||
CardCollection list = CardLists.filter(zoneRestriction != 1 ? game.getCardsIn(this.from) :
|
||||
payer.getCardsIn(this.from), CardPredicates.canExiledBy(ability, effect));
|
||||
|
||||
if (this.payCostFromSource()) {
|
||||
return list.contains(source);
|
||||
@@ -216,7 +216,6 @@ public class CostExile extends CostPartWithList {
|
||||
}
|
||||
|
||||
if (totalCMC) {
|
||||
int needed = Integer.parseInt(this.getAmount().split("\\+")[0]);
|
||||
if (totalM.equals("X") && ability.getXManaCostPaid() == null) { // X hasn't yet been decided, let it pass
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
case MAIN1:
|
||||
{
|
||||
if (playerTurn.isArchenemy()) {
|
||||
playerTurn.setSchemeInMotion();
|
||||
playerTurn.setSchemeInMotion(null);
|
||||
}
|
||||
if (playerTurn.hasRadiationEffect()) {
|
||||
handleRadiation();
|
||||
|
||||
@@ -266,7 +266,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return activeScheme;
|
||||
}
|
||||
|
||||
public void setSchemeInMotion() {
|
||||
public void setSchemeInMotion(SpellAbility cause) {
|
||||
if (StaticAbilityCantSetSchemesInMotion.any(getGame())) {
|
||||
return;
|
||||
}
|
||||
@@ -280,10 +280,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield());
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard());
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
activeScheme = getZone(ZoneType.SchemeDeck).get(0);
|
||||
game.getAction().moveTo(ZoneType.Command, activeScheme, null, moveParams);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(activeScheme, cause);
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
@@ -2690,7 +2689,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
game.getView().updatePlanarPlayer(getView());
|
||||
|
||||
for (Card c : destinations) {
|
||||
currentPlanes.add(game.getAction().moveTo(getZone(ZoneType.Command), c, sa));
|
||||
currentPlanes.add(game.getAction().moveTo(getZone(ZoneType.Command), c, sa, AbilityKey.newMap()));
|
||||
planeswalkedToThisTurn.add(c);
|
||||
}
|
||||
|
||||
@@ -2712,7 +2711,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
for (final Card plane : currentPlanes) {
|
||||
plane.clearControllers();
|
||||
game.getAction().moveTo(ZoneType.PlanarDeck, plane, -1, null);
|
||||
game.getAction().moveTo(ZoneType.PlanarDeck, plane, -1, null, AbilityKey.newMap());
|
||||
}
|
||||
currentPlanes.clear();
|
||||
}
|
||||
@@ -3801,7 +3800,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
if (c.isInZone(ZoneType.Sideboard)) { // Sideboard Lesson to Hand
|
||||
game.getAction().reveal(new CardCollection(c), c.getOwner(), true);
|
||||
Card moved = game.getAction().moveTo(ZoneType.Hand, c, sa, params);
|
||||
game.getAction().moveTo(ZoneType.Hand, c, sa, params);
|
||||
} else if (c.isInZone(ZoneType.Hand)) { // Discard and Draw
|
||||
boolean firstDiscard = getNumDiscardedThisTurn() == 0;
|
||||
if (discard(c, sa, true, params) != null) {
|
||||
|
||||
@@ -250,10 +250,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
|
||||
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Stack");
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null, null);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
game.getAction().moveToCommand(eff, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class StaticAbilityCantExile {
|
||||
|
||||
static String MODE = "CantExile";
|
||||
|
||||
public static boolean cantExile(final Card card, final SpellAbility cause, final boolean effect) {
|
||||
final Game game = card.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (!stAb.checkConditions(MODE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (applyCantExileAbility(stAb, card, cause, effect)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean applyCantExileAbility(final StaticAbility stAb, final Card card, final SpellAbility cause, final boolean effect) {
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
return false;
|
||||
}
|
||||
if (stAb.hasParam("ForCost")) {
|
||||
if ("True".equalsIgnoreCase(stAb.getParam("ForCost")) == effect) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidCause", cause)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:The Master, Multiplied
|
||||
ManaCost:4 B R
|
||||
Types:Legendary Creature Time Lord Rogue
|
||||
PT:4/3
|
||||
K:Myriad
|
||||
S:Mode$ IgnoreLegendRule | ValidCard$ Creature.YouCtrl+token | Description$ The "legend rule" doesn't apply to creature tokens you control.
|
||||
S:Mode$ CantSacrifice | ValidCard$ Creature.YouCtrl+token | ValidCause$ Triggered.YouCtrl | ForCost$ False | Description$ Triggered abilities you control can't cause you to sacrifice or exile creature tokens you control.
|
||||
S:Mode$ CantExile | ValidCard$ Creature.YouCtrl+token | ValidCause$ Triggered.YouCtrl | ForCost$ False | Secondary$ True | Description$ Triggered abilities you control can't cause you to sacrifice or exile creature tokens you control.
|
||||
Oracle:Myriad\nThe "legend rule" doesn't apply to creature tokens you control.\nTriggered abilities you control can't cause you to sacrifice or exile creature tokens you control.
|
||||
@@ -81,7 +81,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public PaymentDecision visit(final CostCollectEvidence cost) {
|
||||
CardCollection list = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
||||
CardCollection list = CardLists.filter(player.getCardsIn(ZoneType.Graveyard), CardPredicates.canExiledBy(ability, isEffect()));
|
||||
final int total = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||
final InputSelectCardsFromList inp =
|
||||
new InputSelectCardsFromList(controller, 0, list.size(), list, ability, total);
|
||||
@@ -239,6 +239,9 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public PaymentDecision visit(final CostExile cost) {
|
||||
if (cost.payCostFromSource()) {
|
||||
if (!source.canExiledBy(ability, isEffect())) {
|
||||
return null;
|
||||
}
|
||||
return source.getZone() == player.getZone(cost.from.get(0)) && confirmAction(cost, Localizer.getInstance().getMessage("lblExileConfirm", CardTranslation.getTranslatedName(source.getName()))) ? PaymentDecision.card(source) : null;
|
||||
}
|
||||
|
||||
@@ -274,6 +277,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(list);
|
||||
}
|
||||
list = CardLists.getValidCards(list, type.split(";"), player, source, ability);
|
||||
list = CardLists.filter(list, CardPredicates.canExiledBy(ability, isEffect()));
|
||||
|
||||
if (totalCMC) {
|
||||
int needed = Integer.parseInt(cost.getAmount().split("\\+")[0]);
|
||||
|
||||
Reference in New Issue
Block a user