diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 260ede11f3f..465f6c86b91 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -446,7 +446,7 @@ public class AiController { CardCollection nonLandsInHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), Predicates.not(CardPredicates.Presets.LANDS)); // Some considerations for Momir/MoJhoSto - boolean hasMomir = player.getZone(ZoneType.Command).contains(CardPredicates.nameEquals("Momir Vig, Simic Visionary Avatar")); + boolean hasMomir = player.isCardInCommand("Momir Vig, Simic Visionary Avatar"); if (hasMomir && nonLandsInHand.isEmpty()) { // Only do this if we have an all-basic land hand, which covers both stock Momir and MoJhoSto modes // and also a custom Vanguard setup with a customized basic land deck and Momir as the avatar. @@ -1534,7 +1534,7 @@ public class AiController { } private boolean isSafeToHoldLandDropForMain2(Card landToPlay) { - boolean hasMomir = player.getZone(ZoneType.Command).contains(CardPredicates.nameEquals("Momir Vig, Simic Visionary Avatar")); + boolean hasMomir = player.isCardInCommand("Momir Vig, Simic Visionary Avatar"); if (hasMomir) { // Don't do this in Momir variants since it messes with the AI decision making for the avatar. return false; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index e286ef8efb8..3e7def1b3a1 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2054,7 +2054,7 @@ public class ComputerUtil { return finalHandSize; } - CardCollectionView library = ai.getZone(ZoneType.Library).getCards(); + CardCollectionView library = ai.getCardsIn(ZoneType.Library); int landsInDeck = CardLists.count(library, CardPredicates.isType("Land")); // no land deck, can't do anything better diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 2abe0ccd427..d677c7fc8f0 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -200,13 +200,13 @@ public abstract class GameState { // Mark the cards that need their ID remembered for various reasons cardsReferencedByID.clear(); for (ZoneType zone : ZONES.keySet()) { - for (Card card : game.getCardsIn(zone)) { + for (Card card : game.getCardsIncludePhasingIn(zone)) { if (card.getExiledWith() != null) { // Remember the ID of the card that exiled this card cardsReferencedByID.add(card.getExiledWith()); } if (zone == ZoneType.Battlefield) { - if (card.hasCardAttachments()) { + if (!card.getAllAttachedCards().isEmpty()) { // Remember the ID of cards that have attachments cardsReferencedByID.add(card); } @@ -240,7 +240,7 @@ public abstract class GameState { // if the zone had no cards in it (e.g. empty hand). aiCardTexts.put(zone, ""); humanCardTexts.put(zone, ""); - for (Card card : game.getCardsIn(zone)) { + for (Card card : game.getCardsIncludePhasingIn(zone)) { if (card.getName().equals("Puzzle Goal") && card.getOracleText().contains("New Puzzle")) { puzzleCreatorState = true; } @@ -264,7 +264,7 @@ public abstract class GameState { return; } - if (!c.getMergedCards().isEmpty()) { + if (c.hasMergedCard()) { // we have to go by the current top card name here newText.append(c.getTopMergedCard().getPaperCard().getName()); } else { @@ -297,7 +297,8 @@ public abstract class GameState { newText.append("|Monstrous"); } if (c.isPhasedOut()) { - newText.append("|PhasedOut"); + newText.append("|PhasedOut:"); + newText.append(c.getPhasedOut().isAI() ? "AI" : "HUMAN"); } if (c.isFaceDown()) { newText.append("|FaceDown"); @@ -1328,7 +1329,10 @@ public abstract class GameState { } else if (info.startsWith("Monstrous")) { c.setMonstrous(true); } else if (info.startsWith("PhasedOut")) { - c.setPhasedOut(true); + String tgt = info.substring(info.indexOf(':') + 1); + Player human = player.getGame().getPlayers().get(0); + Player ai = player.getGame().getPlayers().get(1); + c.setPhasedOut(tgt.equalsIgnoreCase("AI") ? ai : human); } else if (info.startsWith("Counters:")) { applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1)); } else if (info.startsWith("SummonSick")) { diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 12f9c33d484..57d50d3a087 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -797,7 +797,7 @@ public class PlayerControllerAi extends PlayerController { case "Never": return false; case "NothingRemembered": - if (source.getRememberedCount() == 0) { + if (!source.hasRemembered()) { return true; } else { Card rem = (Card) source.getFirstRemembered(); @@ -807,7 +807,7 @@ public class PlayerControllerAi extends PlayerController { } break; case "BetterTgtThanRemembered": - if (source.getRememberedCount() > 0) { + if (source.hasRemembered()) { Card rem = (Card) source.getFirstRemembered(); // avoid pumping opponent creature if (!rem.isInPlay() || rem.getController().isOpponentOf(source.getController())) { diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 8ab2a0474bc..59506328d64 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -559,7 +559,7 @@ public class Game { CardCollection cards = new CardCollection(); for (final Player p : getPlayers()) { - cards.addAll(p.getCardsIncludePhasingIn(zone)); + cards.addAll(p.getCardsIn(zone, false)); } return cards; } diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 1ce87661d78..1f1fa1380d1 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -419,10 +419,16 @@ public abstract class SpellAbilityEffect { protected static void addForgetCounterTrigger(final Card card, final String counterType) { String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True"; + String trig2 = "Mode$ PhaseOut | TriggerZones$ Command | ValidCard$ Card.phasedOutIsRemembered | Static$ True"; + + final SpellAbility forgetSA = getForgetSpellAbility(card); final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); - parsedTrigger.setOverridingAbility(getForgetSpellAbility(card)); + final Trigger parsedTrigger2 = TriggerHandler.parseTrigger(trig2, card, true); + parsedTrigger.setOverridingAbility(forgetSA); + parsedTrigger2.setOverridingAbility(forgetSA); card.addTrigger(parsedTrigger); + card.addTrigger(parsedTrigger2); } protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) { @@ -764,6 +770,9 @@ public abstract class SpellAbilityEffect { } protected static void addUntilCommand(final SpellAbility sa, GameCommand until) { + addUntilCommand(sa, until, sa.getActivatingPlayer()); + } + protected static void addUntilCommand(final SpellAbility sa, GameCommand until, Player controller) { Card host = sa.getHostCard(); final Game game = host.getGame(); final String duration = sa.getParam("Duration"); @@ -774,19 +783,25 @@ public abstract class SpellAbilityEffect { if ("UntilEndOfCombat".equals(duration)) { game.getEndOfCombat().addUntil(until); + } else if ("UntilEndOfCombatYourNextTurn".equals(duration)) { + game.getEndOfCombat().registerUntilEnd(controller, until); } else if ("UntilYourNextUpkeep".equals(duration)) { - game.getUpkeep().addUntil(sa.getActivatingPlayer(), until); + game.getUpkeep().addUntil(controller, until); } else if ("UntilTheEndOfYourNextUpkeep".equals(duration)) { if (game.getPhaseHandler().is(PhaseType.UPKEEP)) { - game.getUpkeep().registerUntilEnd(host.getController(), until); + game.getUpkeep().registerUntilEnd(controller, until); } else { - game.getUpkeep().addUntilEnd(host.getController(), until); + game.getUpkeep().addUntilEnd(controller, until); } - } else if ("UntilTheEndOfYourNextTurn".equals(duration)) { - if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) { - game.getEndOfTurn().registerUntilEnd(sa.getActivatingPlayer(), until); + } else if ("UntilYourNextEndStep".equals(duration)) { + game.getEndOfTurn().addUntil(controller, until); + } else if ("UntilYourNextTurn".equals(duration)) { + game.getCleanup().addUntil(controller, until); + } else if ("UntilTheEndOfYourNextTurn".equals(duration)) { + if (game.getPhaseHandler().isPlayerTurn(controller)) { + game.getEndOfTurn().registerUntilEnd(controller, until); } else { - game.getEndOfTurn().addUntilEnd(sa.getActivatingPlayer(), until); + game.getEndOfTurn().addUntilEnd(controller, until); } } else if ("UntilTheEndOfTargetedNextTurn".equals(duration)) { Player targeted = sa.getTargets().getFirstTargetedPlayer(); @@ -795,6 +810,17 @@ public abstract class SpellAbilityEffect { } else { game.getEndOfTurn().addUntilEnd(targeted, until); } + } else if ("ThisTurnAndNextTurn".equals(duration)) { + game.getEndOfTurn().addUntil(new GameCommand() { + private static final long serialVersionUID = -5054153666503075717L; + + @Override + public void run() { + game.getEndOfTurn().addUntil(until); + } + }); + } else if ("UntilStateBasedActionChecked".equals(duration)) { + game.addSBACheckedCommand(until); } else if (duration != null && duration.startsWith("UntilAPlayerCastSpell")) { game.getStack().addCastCommand(duration.split(" ")[1], until); } else if ("UntilHostLeavesPlay".equals(duration)) { @@ -805,9 +831,8 @@ public abstract class SpellAbilityEffect { } else if ("UntilLoseControlOfHost".equals(duration)) { host.addLeavesPlayCommand(until); host.addChangeControllerCommand(until); - } else if ("UntilYourNextTurn".equals(duration)) { - game.getCleanup().addUntil(sa.getActivatingPlayer(), until); } else if ("UntilUntaps".equals(duration)) { + host.addLeavesPlayCommand(until); host.addUntapCommand(until); } else if ("UntilUnattached".equals(duration)) { host.addLeavesPlayCommand(until); //if it leaves play, it's unattached diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java index 061648c54a8..ec6e62531b9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java @@ -84,8 +84,7 @@ public class ControlGainEffect extends SpellAbilityEffect { return sb.toString(); } - private static void doLoseControl(final Card c, final Card host, - final boolean tapOnLose, final long tStamp) { + private static void doLoseControl(final Card c, final Card host, final long tStamp) { if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) { return; } @@ -94,10 +93,6 @@ public class ControlGainEffect extends SpellAbilityEffect { c.removeTempController(tStamp); game.getAction().controllerChangeZoneCorrection(c); - - if (tapOnLose) { - c.tap(false); - } } host.removeGainControlTargets(c); } @@ -108,7 +103,6 @@ public class ControlGainEffect extends SpellAbilityEffect { final Player activator = sa.getActivatingPlayer(); final boolean bUntap = sa.hasParam("Untap"); - final boolean bTapOnLose = sa.hasParam("TapOnLose"); final boolean remember = sa.hasParam("RememberControlled"); final boolean forget = sa.hasParam("ForgetControlled"); final List keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null; @@ -210,7 +204,7 @@ public class ControlGainEffect extends SpellAbilityEffect { } if (lose != null) { - final GameCommand loseControl = getLoseControlCommand(tgtC, tStamp, bTapOnLose, source); + final GameCommand loseControl = getLoseControlCommand(tgtC, tStamp, source); if (lose.contains("LeavesPlay") && source != tgtC) { // Only return control if host and target are different cards source.addLeavesPlayCommand(loseControl); } @@ -280,14 +274,13 @@ public class ControlGainEffect extends SpellAbilityEffect { * a {@link forge.game.player.Player} object. * @return a {@link forge.GameCommand} object. */ - private static GameCommand getLoseControlCommand(final Card c, - final long tStamp, final boolean bTapOnLose, final Card hostCard) { + private static GameCommand getLoseControlCommand(final Card c, final long tStamp, final Card hostCard) { final GameCommand loseControl = new GameCommand() { private static final long serialVersionUID = 878543373519872418L; @Override public void run() { - doLoseControl(c, hostCard, bTapOnLose, tStamp); + doLoseControl(c, hostCard, tStamp); c.removeChangedSVars(tStamp, 0); } }; diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 80f3544731d..51da7b83dc2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -55,13 +55,16 @@ public class EffectEffect extends SpellAbilityEffect { List effectOwner = null; final String duration = sa.getParam("Duration"); - if (((duration != null && duration.startsWith("UntilHostLeavesPlay")) || "UntilLoseControlOfHost".equals(duration)) + if (((duration != null && duration.startsWith("UntilHostLeavesPlay")) || "UntilLoseControlOfHost".equals(duration) || "UntilUntaps".equals(duration)) && !(hostCard.isInPlay() || hostCard.isInZone(ZoneType.Stack))) { return; } if ("UntilLoseControlOfHost".equals(duration) && hostCard.getController() != sa.getActivatingPlayer()) { return; } + if ("UntilUntaps".equals(duration) && !hostCard.isTapped()) { + return; + } if (sa.hasParam("Abilities")) { effectAbilities = sa.getParam("Abilities").split(","); @@ -94,7 +97,7 @@ public class EffectEffect extends SpellAbilityEffect { if (sa.hasParam("ForgetCounter")) { CounterType cType = CounterType.getType(sa.getParam("ForgetCounter")); - rememberList = new FCollection(CardLists.filter(Iterables.filter(rememberList, Card.class), CardPredicates.hasCounter(cType))); + rememberList = new FCollection<>(CardLists.filter(Iterables.filter(rememberList, Card.class), CardPredicates.hasCounter(cType))); } // don't create Effect if there is no remembered Objects @@ -294,7 +297,6 @@ public class EffectEffect extends SpellAbilityEffect { registerDelayedTrigger(sa, sa.getParam("AtEOT"), Lists.newArrayList(hostCard)); } - // Duration if (duration == null || !duration.equals("Permanent")) { final GameCommand endEffect = new GameCommand() { private static final long serialVersionUID = -5861759814760561373L; @@ -305,44 +307,7 @@ public class EffectEffect extends SpellAbilityEffect { } }; - if (duration == null || duration.equals("EndOfTurn")) { - game.getEndOfTurn().addUntil(endEffect); - } else if (duration.equals("UntilHostLeavesPlay")) { - hostCard.addLeavesPlayCommand(endEffect); - } else if (duration.equals("UntilHostLeavesPlayOrEOT")) { - game.getEndOfTurn().addUntil(endEffect); - hostCard.addLeavesPlayCommand(endEffect); - } else if (duration.equals("UntilLoseControlOfHost")) { - hostCard.addLeavesPlayCommand(endEffect); - hostCard.addChangeControllerCommand(endEffect); - } else if (duration.equals("UntilYourNextTurn")) { - game.getCleanup().addUntil(controller, endEffect); - } else if (duration.equals("UntilYourNextUpkeep")) { - game.getUpkeep().addUntil(controller, endEffect); - } else if (duration.equals("UntilEndOfCombat")) { - game.getEndOfCombat().addUntil(endEffect); - } else if (duration.equals("UntilEndOfCombatYourNextTurn")) { - game.getEndOfCombat().registerUntilEnd(controller, endEffect); - } else if (duration.equals("UntilYourNextEndStep")) { - game.getEndOfTurn().addUntil(controller, endEffect); - } else if (duration.equals("UntilTheEndOfYourNextTurn")) { - if (game.getPhaseHandler().isPlayerTurn(controller)) { - game.getEndOfTurn().registerUntilEnd(controller, endEffect); - } else { - game.getEndOfTurn().addUntilEnd(controller, endEffect); - } - } else if (duration.equals("ThisTurnAndNextTurn")) { - game.getEndOfTurn().addUntil(new GameCommand() { - private static final long serialVersionUID = -5054153666503075717L; - - @Override - public void run() { - game.getEndOfTurn().addUntil(endEffect); - } - }); - } else if (duration.equals("UntilStateBasedActionChecked")) { - game.addSBACheckedCommand(endEffect); - } + addUntilCommand(sa, endEffect, controller); } if (sa.hasParam("ImprintOnHost")) { diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 63904d99354..fe0c36e6c23 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -233,7 +233,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // set for transform and meld, needed for clone effects private boolean backside = false; - private boolean phasedOut = false; + private Player phasedOut; private boolean directlyPhasedOut = true; private boolean wontPhaseInNormal = false; @@ -4998,9 +4998,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final boolean isPhasedOut() { + return phasedOut != null; + } + public final boolean isPhasedOut(Player turn) { + return turn.equals(phasedOut); + } + public final Player getPhasedOut() { return phasedOut; } - public final void setPhasedOut(final boolean phasedOut0) { + public final void setPhasedOut(final Player phasedOut0) { if (phasedOut == phasedOut0) { return; } phasedOut = phasedOut0; view.updatePhasedOut(this); @@ -5040,15 +5046,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } private boolean switchPhaseState(final boolean fromUntapStep) { - if (phasedOut && StaticAbilityCantPhaseIn.cantPhaseIn(this)) { + if (isPhasedOut() && StaticAbilityCantPhaseIn.cantPhaseIn(this)) { return false; } - if (!phasedOut && StaticAbilityCantPhaseOut.cantPhaseOut(this)) { + if (!isPhasedOut() && StaticAbilityCantPhaseOut.cantPhaseOut(this)) { return false; } - if (phasedOut && fromUntapStep && wontPhaseInNormal) { + if (isPhasedOut() && fromUntapStep && wontPhaseInNormal) { return false; } @@ -5061,16 +5067,23 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // when it doesn't exist the game will no longer see it as tapped runUntapCommands(); // TODO CR 702.26f need to run LeavesPlay + changeController commands but only when worded "for as long as" + + // these links also break + clearEncodedCards(); + if (isPaired()) { + getPairedWith().setPairedWith(null); + setPairedWith(null); + } } - setPhasedOut(!phasedOut); + setPhasedOut(isPhasedOut() ? null : getController()); final Combat combat = getGame().getCombat(); - if (combat != null && phasedOut) { + if (combat != null && isPhasedOut()) { combat.saveLKI(this); combat.removeFromCombat(this); } - if (!phasedOut) { + if (!isPhasedOut()) { // Just phased in, time to run the phased in trigger getGame().getTriggerHandler().registerActiveTrigger(this, false); getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false); diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 6b8fe6423be..653a082d921 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -48,8 +48,12 @@ public class CardProperty { final Player controller = lki.getController(); // CR 702.25b if card is phased out it will not count unless specifically asked for - if (card.isPhasedOut() && !property.contains("phasedOut")) { - return false; + if (card.isPhasedOut()) { + if (property.startsWith("phasedOut")) { + property = property.substring(9); + } else { + return false; + } } // by name can also have color names, so needs to happen before colors. diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index dd5f4a5020f..b9492342cee 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -239,7 +239,7 @@ public final class CardUtil { newCopy.setCounters(Maps.newHashMap(in.getCounters())); newCopy.setColor(in.getColor().getColor()); - newCopy.setPhasedOut(in.isPhasedOut()); + newCopy.setPhasedOut(in.getPhasedOut()); newCopy.setDamageHistory(in.getDamageHistory()); newCopy.setDamageReceivedThisTurn(in.getDamageReceivedThisTurn()); diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index 71c8c44a975..ea61da97ce0 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -192,7 +192,7 @@ public class PhaseHandler implements java.io.Serializable { game.fireEvent(new GameEventTurnBegan(playerTurn, turn)); // Tokens starting game in play should suffer from Sum. Sickness - for (final Card c : playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield)) { + for (final Card c : playerTurn.getCardsIn(ZoneType.Battlefield, false)) { if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) { c.setSickness(false); } @@ -201,8 +201,8 @@ public class PhaseHandler implements java.io.Serializable { game.getAction().resetActivationsPerTurn(); - final List lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED); - playerTurn.setNumPowerSurgeLands(lands.size()); + final int lands = CardLists.count(playerTurn.getLandsInPlay(), Presets.UNTAPPED); + playerTurn.setNumPowerSurgeLands(lands); } //update tokens game.fireEvent(new GameEventTokenStateUpdate(playerTurn.getTokensInPlay())); diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java index 0ced0a3acfd..dcdd3861e09 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -253,11 +253,11 @@ public class Untap extends Phase { private static void doPhasing(final Player turn) { // Needs to include phased out cards - final List list = CardLists.filter(turn.getCardsIncludePhasingIn(ZoneType.Battlefield), new Predicate() { + final List list = CardLists.filter(turn.getGame().getCardsIncludePhasingIn(ZoneType.Battlefield), new Predicate() { @Override public boolean apply(final Card c) { - return (c.isPhasedOut() && c.isDirectlyPhasedOut()) || c.hasKeyword(Keyword.PHASING); + return (c.isPhasedOut(turn) && c.isDirectlyPhasedOut()) || (c.hasKeyword(Keyword.PHASING) && c.getController().equals(turn)); } }); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 0cb7a39da1a..922af3691e2 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1307,10 +1307,6 @@ public class Player extends GameEntity implements Comparable { return zone == null ? CardCollection.EMPTY : zone.getCards(filterOutPhasedOut); } - public final CardCollectionView getCardsIncludePhasingIn(final ZoneType zone) { - return getCardsIn(zone, false); - } - /** * gets a list of first N cards in the requested zone. This function makes a CardCollectionView from Card[]. */ @@ -2357,9 +2353,8 @@ public class Player extends GameEntity implements Comparable { } public CardCollectionView getColoredCardsInPlay(final String color) { - return CardLists.getColor(getCardsIn(ZoneType.Battlefield), MagicColor.fromName(color)); + return getColoredCardsInPlay(MagicColor.fromName(color)); } - public CardCollectionView getColoredCardsInPlay(final byte color) { return CardLists.getColor(getCardsIn(ZoneType.Battlefield), color); } @@ -2631,7 +2626,7 @@ public class Player extends GameEntity implements Comparable { public final void resetCombatantsThisCombat() { // resets the status of attacked/blocked this phase - CardCollectionView list = getCardsIn(ZoneType.Battlefield); + CardCollectionView list = getCardsIn(ZoneType.Battlefield, false); for (Card c : list) { if (c.getDamageHistory().getCreatureAttackedThisCombat() > 0) { diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index 0ee21b66df9..aa23631b709 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -200,10 +200,10 @@ public class ReplacementHandler { if (!replacementEffect.hasRun() && (layer == null || replacementEffect.getLayer() == layer) && event.equals(replacementEffect.getMode()) - && replacementEffect.requirementsCheck(game) - && replacementEffect.canReplace(runParams) && !possibleReplacers.contains(replacementEffect) - && replacementEffect.zonesCheck(cardZone)) { + && replacementEffect.zonesCheck(cardZone) + && replacementEffect.requirementsCheck(game) + && replacementEffect.canReplace(runParams)) { possibleReplacers.add(replacementEffect); } } diff --git a/forge-gui/res/cardsfolder/e/ertais_familiar.txt b/forge-gui/res/cardsfolder/e/ertais_familiar.txt index 1b3fe0b2a0f..a07a5a3055d 100644 --- a/forge-gui/res/cardsfolder/e/ertais_familiar.txt +++ b/forge-gui/res/cardsfolder/e/ertais_familiar.txt @@ -3,7 +3,7 @@ ManaCost:1 U Types:Creature Illusion PT:2/2 K:Phasing -T:Mode$ PhaseOut | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When CARDNAME phases out or leaves the battlefield, mill three cards. +T:Mode$ PhaseOut | ValidCard$ Card.phasedOutSelf | Execute$ TrigMill | TriggerDescription$ When CARDNAME phases out or leaves the battlefield, mill three cards. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | TriggerZones$ Battlefield | Secondary$ True | Execute$ TrigMill | TriggerDescription$ When CARDNAME phases out or leaves the battlefield, mill three cards. SVar:TrigMill:DB$ Mill | NumCards$ 3 A:AB$ Effect | Cost$ U | RememberObjects$ Self | ForgetOnMoved$ Battlefield | StaticAbilities$ CantPhaseOut | Duration$ UntilYourNextUpkeep | SpellDescription$ Until your next upkeep, CARDNAME can't phase out. diff --git a/forge-gui/res/cardsfolder/m/magus_of_the_unseen.txt b/forge-gui/res/cardsfolder/m/magus_of_the_unseen.txt index 4106699d610..2cb21b32135 100644 --- a/forge-gui/res/cardsfolder/m/magus_of_the_unseen.txt +++ b/forge-gui/res/cardsfolder/m/magus_of_the_unseen.txt @@ -2,6 +2,8 @@ Name:Magus of the Unseen ManaCost:1 U Types:Creature Human Wizard PT:1/1 -A:AB$ GainControl | Cost$ 1 U T | ValidTgts$ Artifact.OppCtrl | TgtPrompt$ Select target artifact opponent controls | LoseControl$ EOT | AddKWs$ Haste | Untap$ True | TapOnLose$ True | SpellDescription$ Untap target artifact an opponent controls and gain control of it until end of turn. It gains haste until end of turn. When you lose control of the artifact, tap it. +A:AB$ GainControl | Cost$ 1 U T | ValidTgts$ Artifact.OppCtrl | TgtPrompt$ Select target artifact opponent controls | LoseControl$ EOT | AddKWs$ Haste | Untap$ True | SubAbility$ DBDelay | SpellDescription$ Untap target artifact an opponent controls and gain control of it until end of turn. It gains haste until end of turn. +SVar:DBDelay:DB$ DelayedTrigger | Mode$ ChangesController | ValidCard$ Card.IsTriggerRemembered | ValidOriginalController$ You | RememberObjects$ Targeted | Execute$ TrigTap | SpellDescription$ When you lose control of the artifact, tap it. +SVar:TrigTap:DB$ Tap | Defined$ DelayTriggerRememberedLKI AI:RemoveDeck:All Oracle:{1}{U}, {T}: Untap target artifact an opponent controls and gain control of it until end of turn. It gains haste until end of turn. When you lose control of the artifact, tap it. diff --git a/forge-gui/res/cardsfolder/r/ray_of_command.txt b/forge-gui/res/cardsfolder/r/ray_of_command.txt index bcf6c4979f6..57c7f2e8cde 100644 --- a/forge-gui/res/cardsfolder/r/ray_of_command.txt +++ b/forge-gui/res/cardsfolder/r/ray_of_command.txt @@ -1,5 +1,7 @@ Name:Ray of Command ManaCost:3 U Types:Instant -A:SP$ GainControl | Cost$ 3 U | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature opponent controls | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | TapOnLose$ True | SpellDescription$ Untap target creature an opponent controls and gain control of it until end of turn. That creature gains haste until end of turn. When you lose control of the creature, tap it. +A:SP$ GainControl | Cost$ 3 U | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature opponent controls | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SubAbility$ DBDelay | SpellDescription$ Untap target creature an opponent controls and gain control of it until end of turn. That creature gains haste until end of turn. +SVar:DBDelay:DB$ DelayedTrigger | Mode$ ChangesController | ValidCard$ Card.IsTriggerRemembered | ValidOriginalController$ You | RememberObjects$ Targeted | Execute$ TrigTap | SpellDescription$ When you lose control of the creature, tap it. +SVar:TrigTap:DB$ Tap | Defined$ DelayTriggerRememberedLKI Oracle:Untap target creature an opponent controls and gain control of it until end of turn. That creature gains haste until end of turn. When you lose control of the creature, tap it. diff --git a/forge-gui/res/cardsfolder/t/teferi_timeless_voyager.txt b/forge-gui/res/cardsfolder/t/teferi_timeless_voyager.txt index 25ab83e37b1..6995ac15095 100644 --- a/forge-gui/res/cardsfolder/t/teferi_timeless_voyager.txt +++ b/forge-gui/res/cardsfolder/t/teferi_timeless_voyager.txt @@ -6,6 +6,6 @@ A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | SpellDescription A:AB$ ChangeZone | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Library | LibraryPosition$ 0 | SpellDescription$ Put target creature on top of its owner's library. A:AB$ Phases | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Opponent | AllValid$ Creature.TargetedPlayerCtrl | RememberValids$ True | SubAbility$ DBEffect | SpellDescription$ Each creature target opponent controls phases out. SVar:DBEffect:DB$ Effect | Duration$ UntilTheEndOfYourNextTurn | RememberObjects$ Remembered | StaticAbilities$ CantPhaseIn | SubAbility$ DBCleanup | SpellDescription$ Until the end of your next turn, they can't phase in. (Treat them and anything attached to them as though they don't exist.) -SVar:CantPhaseIn:Mode$ CantPhaseIn | ValidCard$ Card.IsRemembered | Description$ Until the end of your next turn, they can't phase in. (Treat them and anything attached to them as though they don't exist.) +SVar:CantPhaseIn:Mode$ CantPhaseIn | ValidCard$ Card.phasedOutIsRemembered | Description$ Until the end of your next turn, they can't phase in. (Treat them and anything attached to them as though they don't exist.) SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True Oracle:[+1]: Draw a card.\n[-3]: Put target creature on top of its owner's library.\n[-8]: Each creature target opponent controls phases out. Until the end of your next turn, they can't phase in. (Treat them and anything attached to them as though they don't exist.) diff --git a/forge-gui/res/cardsfolder/t/teferis_imp.txt b/forge-gui/res/cardsfolder/t/teferis_imp.txt index 2bca628e0a0..0f39331a779 100644 --- a/forge-gui/res/cardsfolder/t/teferis_imp.txt +++ b/forge-gui/res/cardsfolder/t/teferis_imp.txt @@ -4,7 +4,7 @@ Types:Creature Imp PT:1/1 K:Flying K:Phasing -T:Mode$ PhaseOut | ValidCard$ Card.Self | Execute$ TrigDiscard | TriggerDescription$ Whenever CARDNAME phases out, discard a card. +T:Mode$ PhaseOut | ValidCard$ Card.phasedOutSelf | Execute$ TrigDiscard | TriggerDescription$ Whenever CARDNAME phases out, discard a card. SVar:TrigDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 T:Mode$ PhaseIn | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME phases in, draw a card. SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 diff --git a/forge-gui/res/cardsfolder/t/the_phasing_of_zhalfir.txt b/forge-gui/res/cardsfolder/t/the_phasing_of_zhalfir.txt index 1c6ae41df76..0d924256859 100644 --- a/forge-gui/res/cardsfolder/t/the_phasing_of_zhalfir.txt +++ b/forge-gui/res/cardsfolder/t/the_phasing_of_zhalfir.txt @@ -2,11 +2,9 @@ Name:The Phasing of Zhalfir ManaCost:2 U U Types:Enchantment Saga K:Read ahead:3:DBPhase,DBPhase,DBDestroyAll -SVar:DBPhase:DB$ Phases | ValidTgts$ Permanent.nonLand+Other | WontPhaseInNormal$ True | ConditionPresent$ Card.Self | SubAbility$ DBEffect | SpellDescription$ Another target nonland permanent phases out. It can't phase in for as long as you control CARDNAME. -SVar:DBEffect:DB$ Effect | Triggers$ TrigComeBack | RememberObjects$ Targeted | ImprintCards$ Self | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnPhasedIn$ True -SVar:TrigComeBack:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ DBPhaseIn | TriggerZones$ Command | TriggerController$ TriggeredCardController | Static$ True -SVar:DBPhaseIn:DB$ Phases | Defined$ Remembered | PhaseInOrOut$ True | SubAbility$ DBExileSelf -SVar:DBExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self +SVar:DBPhase:DB$ Phases | ValidTgts$ Permanent.nonLand+Other | ConditionPresent$ Card.Self | SubAbility$ DBEffect | SpellDescription$ Another target nonland permanent phases out. It can't phase in for as long as you control CARDNAME. +SVar:DBEffect:DB$ Effect | StaticAbilities$ CantPhaseIn | RememberObjects$ Targeted | ConditionPresent$ Card.Self | Duration$ UntilLoseControlOfHost +SVar:CantPhaseIn:Mode$ CantPhaseIn | ValidCard$ Card.phasedOutIsRemembered | Description$ It can't phase in for as long as you control EFFECTSOURCE. SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature | RememberDestroyed$ True | SubAbility$ DBRepeat | SpellDescription$ Destroy all creatures. For each creature destroyed this way, its controller creates a 2/2 black Phyrexian creature token. SVar:DBRepeat:DB$ RepeatEach | UseImprinted$ True | DefinedCards$ DirectRemembered | RepeatSubAbility$ DBToken | SubAbility$ DBCleanup | ChangeZoneTable$ True SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ ImprintedController | TokenScript$ b_2_2_phyrexian diff --git a/forge-gui/res/cardsfolder/t/time_and_tide.txt b/forge-gui/res/cardsfolder/t/time_and_tide.txt index 5ca4301db22..3f6c038c179 100644 --- a/forge-gui/res/cardsfolder/t/time_and_tide.txt +++ b/forge-gui/res/cardsfolder/t/time_and_tide.txt @@ -1,6 +1,6 @@ Name:Time and Tide ManaCost:U U Types:Instant -A:SP$ Phases | Cost$ U U | AllValid$ Creature.hasKeywordPhasing,Creature.phasedOut | PhaseInOrOut$ True | SpellDescription$ Simultaneously, all phased-out creatures phase in and all creatures with phasing phase out. +A:SP$ Phases | Cost$ U U | AllValid$ Creature.hasKeywordPhasing,Card.phasedOutCreature | PhaseInOrOut$ True | SpellDescription$ Simultaneously, all phased-out creatures phase in and all creatures with phasing phase out. AI:RemoveDeck:All Oracle:Simultaneously, all phased-out creatures phase in and all creatures with phasing phase out. diff --git a/forge-gui/res/cardsfolder/upcoming/disciple_of_caelus_nin.txt b/forge-gui/res/cardsfolder/upcoming/disciple_of_caelus_nin.txt index 2251add2fc4..884061a20e9 100644 --- a/forge-gui/res/cardsfolder/upcoming/disciple_of_caelus_nin.txt +++ b/forge-gui/res/cardsfolder/upcoming/disciple_of_caelus_nin.txt @@ -7,5 +7,5 @@ SVar:TrigRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player | StartingWithActivat SVar:DBChoosePermanent:DB$ ChooseCard | ChoiceTitle$ Choose up to five permanents you control | MinAmount$ 0 | Amount$ 5 | Defined$ Remembered | Choices$ Permanent.RememberedPlayerCtrl | RememberChosen$ True | AILogic$ NotSelf SVar:DBPhaseOut:DB$ Phases | AllValid$ Permanent.Other+IsNotRemembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True | ClearRemembered$ True -S:Mode$ CantPhaseIn | ValidCard$ Permanent | Description$ Permanents can't phase in. +S:Mode$ CantPhaseIn | ValidCard$ Card.phasedOutPermanent | Description$ Permanents can't phase in. Oracle:When Disciple of Caelus Nin enters the battlefield, starting with you, each player chooses up to five permanents they control. All permanents other than Disciple of Caelus Nin that weren't chosen this way phase out.\nPermanents can't phase in.