Merge pull request #1973 from tool4ever/phasingfix

Phasing: store under which control it phased out
This commit is contained in:
Anthony Calosa
2022-11-28 15:17:48 +08:00
committed by GitHub
23 changed files with 113 additions and 112 deletions

View File

@@ -446,7 +446,7 @@ public class AiController {
CardCollection nonLandsInHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), Predicates.not(CardPredicates.Presets.LANDS)); CardCollection nonLandsInHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), Predicates.not(CardPredicates.Presets.LANDS));
// Some considerations for Momir/MoJhoSto // 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()) { if (hasMomir && nonLandsInHand.isEmpty()) {
// Only do this if we have an all-basic land hand, which covers both stock Momir and MoJhoSto modes // 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. // 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) { 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) { if (hasMomir) {
// Don't do this in Momir variants since it messes with the AI decision making for the avatar. // Don't do this in Momir variants since it messes with the AI decision making for the avatar.
return false; return false;

View File

@@ -2054,7 +2054,7 @@ public class ComputerUtil {
return finalHandSize; return finalHandSize;
} }
CardCollectionView library = ai.getZone(ZoneType.Library).getCards(); CardCollectionView library = ai.getCardsIn(ZoneType.Library);
int landsInDeck = CardLists.count(library, CardPredicates.isType("Land")); int landsInDeck = CardLists.count(library, CardPredicates.isType("Land"));
// no land deck, can't do anything better // no land deck, can't do anything better

View File

@@ -200,13 +200,13 @@ public abstract class GameState {
// Mark the cards that need their ID remembered for various reasons // Mark the cards that need their ID remembered for various reasons
cardsReferencedByID.clear(); cardsReferencedByID.clear();
for (ZoneType zone : ZONES.keySet()) { for (ZoneType zone : ZONES.keySet()) {
for (Card card : game.getCardsIn(zone)) { for (Card card : game.getCardsIncludePhasingIn(zone)) {
if (card.getExiledWith() != null) { if (card.getExiledWith() != null) {
// Remember the ID of the card that exiled this card // Remember the ID of the card that exiled this card
cardsReferencedByID.add(card.getExiledWith()); cardsReferencedByID.add(card.getExiledWith());
} }
if (zone == ZoneType.Battlefield) { if (zone == ZoneType.Battlefield) {
if (card.hasCardAttachments()) { if (!card.getAllAttachedCards().isEmpty()) {
// Remember the ID of cards that have attachments // Remember the ID of cards that have attachments
cardsReferencedByID.add(card); cardsReferencedByID.add(card);
} }
@@ -240,7 +240,7 @@ public abstract class GameState {
// if the zone had no cards in it (e.g. empty hand). // if the zone had no cards in it (e.g. empty hand).
aiCardTexts.put(zone, ""); aiCardTexts.put(zone, "");
humanCardTexts.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")) { if (card.getName().equals("Puzzle Goal") && card.getOracleText().contains("New Puzzle")) {
puzzleCreatorState = true; puzzleCreatorState = true;
} }
@@ -264,7 +264,7 @@ public abstract class GameState {
return; return;
} }
if (!c.getMergedCards().isEmpty()) { if (c.hasMergedCard()) {
// we have to go by the current top card name here // we have to go by the current top card name here
newText.append(c.getTopMergedCard().getPaperCard().getName()); newText.append(c.getTopMergedCard().getPaperCard().getName());
} else { } else {
@@ -297,7 +297,8 @@ public abstract class GameState {
newText.append("|Monstrous"); newText.append("|Monstrous");
} }
if (c.isPhasedOut()) { if (c.isPhasedOut()) {
newText.append("|PhasedOut"); newText.append("|PhasedOut:");
newText.append(c.getPhasedOut().isAI() ? "AI" : "HUMAN");
} }
if (c.isFaceDown()) { if (c.isFaceDown()) {
newText.append("|FaceDown"); newText.append("|FaceDown");
@@ -1328,7 +1329,10 @@ public abstract class GameState {
} else if (info.startsWith("Monstrous")) { } else if (info.startsWith("Monstrous")) {
c.setMonstrous(true); c.setMonstrous(true);
} else if (info.startsWith("PhasedOut")) { } 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:")) { } else if (info.startsWith("Counters:")) {
applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1)); applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("SummonSick")) { } else if (info.startsWith("SummonSick")) {

View File

@@ -797,7 +797,7 @@ public class PlayerControllerAi extends PlayerController {
case "Never": case "Never":
return false; return false;
case "NothingRemembered": case "NothingRemembered":
if (source.getRememberedCount() == 0) { if (!source.hasRemembered()) {
return true; return true;
} else { } else {
Card rem = (Card) source.getFirstRemembered(); Card rem = (Card) source.getFirstRemembered();
@@ -807,7 +807,7 @@ public class PlayerControllerAi extends PlayerController {
} }
break; break;
case "BetterTgtThanRemembered": case "BetterTgtThanRemembered":
if (source.getRememberedCount() > 0) { if (source.hasRemembered()) {
Card rem = (Card) source.getFirstRemembered(); Card rem = (Card) source.getFirstRemembered();
// avoid pumping opponent creature // avoid pumping opponent creature
if (!rem.isInPlay() || rem.getController().isOpponentOf(source.getController())) { if (!rem.isInPlay() || rem.getController().isOpponentOf(source.getController())) {

View File

@@ -559,7 +559,7 @@ public class Game {
CardCollection cards = new CardCollection(); CardCollection cards = new CardCollection();
for (final Player p : getPlayers()) { for (final Player p : getPlayers()) {
cards.addAll(p.getCardsIncludePhasingIn(zone)); cards.addAll(p.getCardsIn(zone, false));
} }
return cards; return cards;
} }

View File

@@ -419,10 +419,16 @@ public abstract class SpellAbilityEffect {
protected static void addForgetCounterTrigger(final Card card, final String counterType) { 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 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); 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(parsedTrigger);
card.addTrigger(parsedTrigger2);
} }
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) { 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) { 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(); Card host = sa.getHostCard();
final Game game = host.getGame(); final Game game = host.getGame();
final String duration = sa.getParam("Duration"); final String duration = sa.getParam("Duration");
@@ -774,19 +783,25 @@ public abstract class SpellAbilityEffect {
if ("UntilEndOfCombat".equals(duration)) { if ("UntilEndOfCombat".equals(duration)) {
game.getEndOfCombat().addUntil(until); game.getEndOfCombat().addUntil(until);
} else if ("UntilEndOfCombatYourNextTurn".equals(duration)) {
game.getEndOfCombat().registerUntilEnd(controller, until);
} else if ("UntilYourNextUpkeep".equals(duration)) { } else if ("UntilYourNextUpkeep".equals(duration)) {
game.getUpkeep().addUntil(sa.getActivatingPlayer(), until); game.getUpkeep().addUntil(controller, until);
} else if ("UntilTheEndOfYourNextUpkeep".equals(duration)) { } else if ("UntilTheEndOfYourNextUpkeep".equals(duration)) {
if (game.getPhaseHandler().is(PhaseType.UPKEEP)) { if (game.getPhaseHandler().is(PhaseType.UPKEEP)) {
game.getUpkeep().registerUntilEnd(host.getController(), until); game.getUpkeep().registerUntilEnd(controller, until);
} else { } else {
game.getUpkeep().addUntilEnd(host.getController(), until); game.getUpkeep().addUntilEnd(controller, 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)) { } else if ("UntilTheEndOfYourNextTurn".equals(duration)) {
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) { if (game.getPhaseHandler().isPlayerTurn(controller)) {
game.getEndOfTurn().registerUntilEnd(sa.getActivatingPlayer(), until); game.getEndOfTurn().registerUntilEnd(controller, until);
} else { } else {
game.getEndOfTurn().addUntilEnd(sa.getActivatingPlayer(), until); game.getEndOfTurn().addUntilEnd(controller, until);
} }
} else if ("UntilTheEndOfTargetedNextTurn".equals(duration)) { } else if ("UntilTheEndOfTargetedNextTurn".equals(duration)) {
Player targeted = sa.getTargets().getFirstTargetedPlayer(); Player targeted = sa.getTargets().getFirstTargetedPlayer();
@@ -795,6 +810,17 @@ public abstract class SpellAbilityEffect {
} else { } else {
game.getEndOfTurn().addUntilEnd(targeted, until); 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")) { } else if (duration != null && duration.startsWith("UntilAPlayerCastSpell")) {
game.getStack().addCastCommand(duration.split(" ")[1], until); game.getStack().addCastCommand(duration.split(" ")[1], until);
} else if ("UntilHostLeavesPlay".equals(duration)) { } else if ("UntilHostLeavesPlay".equals(duration)) {
@@ -805,9 +831,8 @@ public abstract class SpellAbilityEffect {
} else if ("UntilLoseControlOfHost".equals(duration)) { } else if ("UntilLoseControlOfHost".equals(duration)) {
host.addLeavesPlayCommand(until); host.addLeavesPlayCommand(until);
host.addChangeControllerCommand(until); host.addChangeControllerCommand(until);
} else if ("UntilYourNextTurn".equals(duration)) {
game.getCleanup().addUntil(sa.getActivatingPlayer(), until);
} else if ("UntilUntaps".equals(duration)) { } else if ("UntilUntaps".equals(duration)) {
host.addLeavesPlayCommand(until);
host.addUntapCommand(until); host.addUntapCommand(until);
} else if ("UntilUnattached".equals(duration)) { } else if ("UntilUnattached".equals(duration)) {
host.addLeavesPlayCommand(until); //if it leaves play, it's unattached host.addLeavesPlayCommand(until); //if it leaves play, it's unattached

View File

@@ -84,8 +84,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
return sb.toString(); return sb.toString();
} }
private static void doLoseControl(final Card c, final Card host, private static void doLoseControl(final Card c, final Card host, final long tStamp) {
final boolean tapOnLose, final long tStamp) {
if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) { if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) {
return; return;
} }
@@ -94,10 +93,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
c.removeTempController(tStamp); c.removeTempController(tStamp);
game.getAction().controllerChangeZoneCorrection(c); game.getAction().controllerChangeZoneCorrection(c);
if (tapOnLose) {
c.tap(false);
}
} }
host.removeGainControlTargets(c); host.removeGainControlTargets(c);
} }
@@ -108,7 +103,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
final Player activator = sa.getActivatingPlayer(); final Player activator = sa.getActivatingPlayer();
final boolean bUntap = sa.hasParam("Untap"); final boolean bUntap = sa.hasParam("Untap");
final boolean bTapOnLose = sa.hasParam("TapOnLose");
final boolean remember = sa.hasParam("RememberControlled"); final boolean remember = sa.hasParam("RememberControlled");
final boolean forget = sa.hasParam("ForgetControlled"); final boolean forget = sa.hasParam("ForgetControlled");
final List<String> keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null; final List<String> keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null;
@@ -210,7 +204,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
} }
if (lose != null) { 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 if (lose.contains("LeavesPlay") && source != tgtC) { // Only return control if host and target are different cards
source.addLeavesPlayCommand(loseControl); source.addLeavesPlayCommand(loseControl);
} }
@@ -280,14 +274,13 @@ public class ControlGainEffect extends SpellAbilityEffect {
* a {@link forge.game.player.Player} object. * a {@link forge.game.player.Player} object.
* @return a {@link forge.GameCommand} object. * @return a {@link forge.GameCommand} object.
*/ */
private static GameCommand getLoseControlCommand(final Card c, private static GameCommand getLoseControlCommand(final Card c, final long tStamp, final Card hostCard) {
final long tStamp, final boolean bTapOnLose, final Card hostCard) {
final GameCommand loseControl = new GameCommand() { final GameCommand loseControl = new GameCommand() {
private static final long serialVersionUID = 878543373519872418L; private static final long serialVersionUID = 878543373519872418L;
@Override @Override
public void run() { public void run() {
doLoseControl(c, hostCard, bTapOnLose, tStamp); doLoseControl(c, hostCard, tStamp);
c.removeChangedSVars(tStamp, 0); c.removeChangedSVars(tStamp, 0);
} }
}; };

View File

@@ -55,13 +55,16 @@ public class EffectEffect extends SpellAbilityEffect {
List<Player> effectOwner = null; List<Player> effectOwner = null;
final String duration = sa.getParam("Duration"); 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))) { && !(hostCard.isInPlay() || hostCard.isInZone(ZoneType.Stack))) {
return; return;
} }
if ("UntilLoseControlOfHost".equals(duration) && hostCard.getController() != sa.getActivatingPlayer()) { if ("UntilLoseControlOfHost".equals(duration) && hostCard.getController() != sa.getActivatingPlayer()) {
return; return;
} }
if ("UntilUntaps".equals(duration) && !hostCard.isTapped()) {
return;
}
if (sa.hasParam("Abilities")) { if (sa.hasParam("Abilities")) {
effectAbilities = sa.getParam("Abilities").split(","); effectAbilities = sa.getParam("Abilities").split(",");
@@ -94,7 +97,7 @@ public class EffectEffect extends SpellAbilityEffect {
if (sa.hasParam("ForgetCounter")) { if (sa.hasParam("ForgetCounter")) {
CounterType cType = CounterType.getType(sa.getParam("ForgetCounter")); CounterType cType = CounterType.getType(sa.getParam("ForgetCounter"));
rememberList = new FCollection<GameObject>(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 // 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)); registerDelayedTrigger(sa, sa.getParam("AtEOT"), Lists.newArrayList(hostCard));
} }
// Duration
if (duration == null || !duration.equals("Permanent")) { if (duration == null || !duration.equals("Permanent")) {
final GameCommand endEffect = new GameCommand() { final GameCommand endEffect = new GameCommand() {
private static final long serialVersionUID = -5861759814760561373L; private static final long serialVersionUID = -5861759814760561373L;
@@ -305,44 +307,7 @@ public class EffectEffect extends SpellAbilityEffect {
} }
}; };
if (duration == null || duration.equals("EndOfTurn")) { addUntilCommand(sa, endEffect, controller);
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);
}
} }
if (sa.hasParam("ImprintOnHost")) { if (sa.hasParam("ImprintOnHost")) {

View File

@@ -233,7 +233,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
// set for transform and meld, needed for clone effects // set for transform and meld, needed for clone effects
private boolean backside = false; private boolean backside = false;
private boolean phasedOut = false; private Player phasedOut;
private boolean directlyPhasedOut = true; private boolean directlyPhasedOut = true;
private boolean wontPhaseInNormal = false; private boolean wontPhaseInNormal = false;
@@ -4998,9 +4998,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public final boolean isPhasedOut() { public final boolean isPhasedOut() {
return phasedOut != null;
}
public final boolean isPhasedOut(Player turn) {
return turn.equals(phasedOut);
}
public final Player getPhasedOut() {
return phasedOut; return phasedOut;
} }
public final void setPhasedOut(final boolean phasedOut0) { public final void setPhasedOut(final Player phasedOut0) {
if (phasedOut == phasedOut0) { return; } if (phasedOut == phasedOut0) { return; }
phasedOut = phasedOut0; phasedOut = phasedOut0;
view.updatePhasedOut(this); view.updatePhasedOut(this);
@@ -5040,15 +5046,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
private boolean switchPhaseState(final boolean fromUntapStep) { private boolean switchPhaseState(final boolean fromUntapStep) {
if (phasedOut && StaticAbilityCantPhaseIn.cantPhaseIn(this)) { if (isPhasedOut() && StaticAbilityCantPhaseIn.cantPhaseIn(this)) {
return false; return false;
} }
if (!phasedOut && StaticAbilityCantPhaseOut.cantPhaseOut(this)) { if (!isPhasedOut() && StaticAbilityCantPhaseOut.cantPhaseOut(this)) {
return false; return false;
} }
if (phasedOut && fromUntapStep && wontPhaseInNormal) { if (isPhasedOut() && fromUntapStep && wontPhaseInNormal) {
return false; return false;
} }
@@ -5061,16 +5067,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
// when it doesn't exist the game will no longer see it as tapped // when it doesn't exist the game will no longer see it as tapped
runUntapCommands(); runUntapCommands();
// TODO CR 702.26f need to run LeavesPlay + changeController commands but only when worded "for as long as" // 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(); final Combat combat = getGame().getCombat();
if (combat != null && phasedOut) { if (combat != null && isPhasedOut()) {
combat.saveLKI(this); combat.saveLKI(this);
combat.removeFromCombat(this); combat.removeFromCombat(this);
} }
if (!phasedOut) { if (!isPhasedOut()) {
// Just phased in, time to run the phased in trigger // Just phased in, time to run the phased in trigger
getGame().getTriggerHandler().registerActiveTrigger(this, false); getGame().getTriggerHandler().registerActiveTrigger(this, false);
getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false); getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false);

View File

@@ -48,9 +48,13 @@ public class CardProperty {
final Player controller = lki.getController(); final Player controller = lki.getController();
// CR 702.25b if card is phased out it will not count unless specifically asked for // CR 702.25b if card is phased out it will not count unless specifically asked for
if (card.isPhasedOut() && !property.contains("phasedOut")) { if (card.isPhasedOut()) {
if (property.startsWith("phasedOut")) {
property = property.substring(9);
} else {
return false; return false;
} }
}
// by name can also have color names, so needs to happen before colors. // by name can also have color names, so needs to happen before colors.
if (property.startsWith("named")) { if (property.startsWith("named")) {

View File

@@ -239,7 +239,7 @@ public final class CardUtil {
newCopy.setCounters(Maps.newHashMap(in.getCounters())); newCopy.setCounters(Maps.newHashMap(in.getCounters()));
newCopy.setColor(in.getColor().getColor()); newCopy.setColor(in.getColor().getColor());
newCopy.setPhasedOut(in.isPhasedOut()); newCopy.setPhasedOut(in.getPhasedOut());
newCopy.setDamageHistory(in.getDamageHistory()); newCopy.setDamageHistory(in.getDamageHistory());
newCopy.setDamageReceivedThisTurn(in.getDamageReceivedThisTurn()); newCopy.setDamageReceivedThisTurn(in.getDamageReceivedThisTurn());

View File

@@ -192,7 +192,7 @@ public class PhaseHandler implements java.io.Serializable {
game.fireEvent(new GameEventTurnBegan(playerTurn, turn)); game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
// Tokens starting game in play should suffer from Sum. Sickness // 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()) { if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
c.setSickness(false); c.setSickness(false);
} }
@@ -201,8 +201,8 @@ public class PhaseHandler implements java.io.Serializable {
game.getAction().resetActivationsPerTurn(); game.getAction().resetActivationsPerTurn();
final List<Card> lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED); final int lands = CardLists.count(playerTurn.getLandsInPlay(), Presets.UNTAPPED);
playerTurn.setNumPowerSurgeLands(lands.size()); playerTurn.setNumPowerSurgeLands(lands);
} }
//update tokens //update tokens
game.fireEvent(new GameEventTokenStateUpdate(playerTurn.getTokensInPlay())); game.fireEvent(new GameEventTokenStateUpdate(playerTurn.getTokensInPlay()));

View File

@@ -253,11 +253,11 @@ public class Untap extends Phase {
private static void doPhasing(final Player turn) { private static void doPhasing(final Player turn) {
// Needs to include phased out cards // Needs to include phased out cards
final List<Card> list = CardLists.filter(turn.getCardsIncludePhasingIn(ZoneType.Battlefield), new Predicate<Card>() { final List<Card> list = CardLists.filter(turn.getGame().getCardsIncludePhasingIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { 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));
} }
}); });

View File

@@ -1307,10 +1307,6 @@ public class Player extends GameEntity implements Comparable<Player> {
return zone == null ? CardCollection.EMPTY : zone.getCards(filterOutPhasedOut); 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[]. * 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<Player> {
} }
public CardCollectionView getColoredCardsInPlay(final String color) { 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) { public CardCollectionView getColoredCardsInPlay(final byte color) {
return CardLists.getColor(getCardsIn(ZoneType.Battlefield), color); return CardLists.getColor(getCardsIn(ZoneType.Battlefield), color);
} }
@@ -2631,7 +2626,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final void resetCombatantsThisCombat() { public final void resetCombatantsThisCombat() {
// resets the status of attacked/blocked this phase // resets the status of attacked/blocked this phase
CardCollectionView list = getCardsIn(ZoneType.Battlefield); CardCollectionView list = getCardsIn(ZoneType.Battlefield, false);
for (Card c : list) { for (Card c : list) {
if (c.getDamageHistory().getCreatureAttackedThisCombat() > 0) { if (c.getDamageHistory().getCreatureAttackedThisCombat() > 0) {

View File

@@ -200,10 +200,10 @@ public class ReplacementHandler {
if (!replacementEffect.hasRun() if (!replacementEffect.hasRun()
&& (layer == null || replacementEffect.getLayer() == layer) && (layer == null || replacementEffect.getLayer() == layer)
&& event.equals(replacementEffect.getMode()) && event.equals(replacementEffect.getMode())
&& replacementEffect.requirementsCheck(game)
&& replacementEffect.canReplace(runParams)
&& !possibleReplacers.contains(replacementEffect) && !possibleReplacers.contains(replacementEffect)
&& replacementEffect.zonesCheck(cardZone)) { && replacementEffect.zonesCheck(cardZone)
&& replacementEffect.requirementsCheck(game)
&& replacementEffect.canReplace(runParams)) {
possibleReplacers.add(replacementEffect); possibleReplacers.add(replacementEffect);
} }
} }

View File

@@ -3,7 +3,7 @@ ManaCost:1 U
Types:Creature Illusion Types:Creature Illusion
PT:2/2 PT:2/2
K:Phasing 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. 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 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. A:AB$ Effect | Cost$ U | RememberObjects$ Self | ForgetOnMoved$ Battlefield | StaticAbilities$ CantPhaseOut | Duration$ UntilYourNextUpkeep | SpellDescription$ Until your next upkeep, CARDNAME can't phase out.

View File

@@ -2,6 +2,8 @@ Name:Magus of the Unseen
ManaCost:1 U ManaCost:1 U
Types:Creature Human Wizard Types:Creature Human Wizard
PT:1/1 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 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. 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.

View File

@@ -1,5 +1,7 @@
Name:Ray of Command Name:Ray of Command
ManaCost:3 U ManaCost:3 U
Types:Instant 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. 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.

View File

@@ -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$ 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. 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: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 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.) 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.)

View File

@@ -4,7 +4,7 @@ Types:Creature Imp
PT:1/1 PT:1/1
K:Flying K:Flying
K:Phasing 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 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. T:Mode$ PhaseIn | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME phases in, draw a card.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1

View File

@@ -2,11 +2,9 @@ Name:The Phasing of Zhalfir
ManaCost:2 U U ManaCost:2 U U
Types:Enchantment Saga Types:Enchantment Saga
K:Read ahead:3:DBPhase,DBPhase,DBDestroyAll 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: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 | Triggers$ TrigComeBack | RememberObjects$ Targeted | ImprintCards$ Self | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnPhasedIn$ True SVar:DBEffect:DB$ Effect | StaticAbilities$ CantPhaseIn | RememberObjects$ Targeted | ConditionPresent$ Card.Self | Duration$ UntilLoseControlOfHost
SVar:TrigComeBack:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ DBPhaseIn | TriggerZones$ Command | TriggerController$ TriggeredCardController | Static$ True SVar:CantPhaseIn:Mode$ CantPhaseIn | ValidCard$ Card.phasedOutIsRemembered | Description$ It can't phase in for as long as you control EFFECTSOURCE.
SVar:DBPhaseIn:DB$ Phases | Defined$ Remembered | PhaseInOrOut$ True | SubAbility$ DBExileSelf
SVar:DBExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self
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: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: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 SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ ImprintedController | TokenScript$ b_2_2_phyrexian

View File

@@ -1,6 +1,6 @@
Name:Time and Tide Name:Time and Tide
ManaCost:U U ManaCost:U U
Types:Instant 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 AI:RemoveDeck:All
Oracle:Simultaneously, all phased-out creatures phase in and all creatures with phasing phase out. Oracle:Simultaneously, all phased-out creatures phase in and all creatures with phasing phase out.

View File

@@ -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: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:DBPhaseOut:DB$ Phases | AllValid$ Permanent.Other+IsNotRemembered | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True | ClearRemembered$ True 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. 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.