Phasing: store under which control it phased out

This commit is contained in:
tool4EvEr
2022-11-27 12:21:25 +01:00
parent 58ea27bde5
commit fbd2b1eaee
13 changed files with 89 additions and 87 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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")) {

View File

@@ -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())) {

View File

@@ -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;
}

View File

@@ -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

View File

@@ -55,13 +55,16 @@ public class EffectEffect extends SpellAbilityEffect {
List<Player> 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<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
@@ -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")) {

View File

@@ -233,7 +233,7 @@ public class Card extends GameEntity implements Comparable<Card>, 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<Card>, 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<Card>, 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<Card>, 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);

View File

@@ -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());

View File

@@ -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<Card> 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()));

View File

@@ -253,11 +253,11 @@ public class Untap extends Phase {
private static void doPhasing(final Player turn) {
// 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
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);
}
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<Player> {
}
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<Player> {
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) {

View File

@@ -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);
}
}