diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 9f78b5b3336..e0c01f01feb 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -676,7 +676,6 @@ public class AiAttackController { * @return a {@link forge.game.combat.Combat} object. */ public final void declareAttackers(final Combat combat) { - if (this.attackers.isEmpty()) { return; } diff --git a/forge-core/src/main/java/forge/card/CardAiHints.java b/forge-core/src/main/java/forge/card/CardAiHints.java index a7b2c8e7255..746f7297a4f 100644 --- a/forge-core/src/main/java/forge/card/CardAiHints.java +++ b/forge-core/src/main/java/forge/card/CardAiHints.java @@ -6,7 +6,6 @@ package forge.card; */ public class CardAiHints { - private final boolean isRemovedFromAIDecks; private final boolean isRemovedFromRandomDecks; private final boolean isRemovedFromNonCommanderDecks; @@ -15,7 +14,6 @@ public class CardAiHints { private final DeckHints deckNeeds; private final DeckHints deckHas; - public CardAiHints(boolean remAi, boolean remRandom, boolean remUnlessCommander, DeckHints dh, DeckHints dn, DeckHints has) { isRemovedFromAIDecks = remAi; isRemovedFromRandomDecks = remRandom; @@ -90,5 +88,4 @@ public class CardAiHints { } } - } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index a4ef170fe74..f322fd5ad39 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -756,7 +756,7 @@ public class Game { // Rule 800.4 Losing a Multiplayer game CardCollectionView cards = this.getCardsInGame(); boolean planarControllerLost = false; - boolean isMultiplayer = this.getPlayers().size() > 2; + boolean isMultiplayer = getPlayers().size() > 2; // 702.142f & 707.9 // If a player leaves the game, all face-down cards that player owns must be revealed to all players. @@ -793,16 +793,16 @@ public class Game { } getAction().ceaseToExist(c, false); // CR 603.2f owner of trigger source lost game - triggerHandler.clearDelayedTrigger(c); + getTriggerHandler().clearDelayedTrigger(c); } else { // return stolen permanents - if (c.getController().equals(p) && c.isInZone(ZoneType.Battlefield)) { + if ((c.getController().equals(p) || c.getZone().getPlayer().equals(p)) && c.isInZone(ZoneType.Battlefield)) { c.removeTempController(p); getAction().controllerChangeZoneCorrection(c); } c.removeTempController(p); if (c.getController().equals(p)) { - this.getAction().exile(c, null); + getAction().exile(c, null); } } } else { @@ -830,7 +830,7 @@ public class Game { } // Remove leftover items from - this.getStack().removeInstancesControlledBy(p); + getStack().removeInstancesControlledBy(p); getTriggerHandler().onPlayerLost(p); 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 fc62803172a..f41a8bf9390 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -21,6 +21,7 @@ import forge.game.GameObject; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; +import forge.game.card.CardUtil; import forge.game.card.CardZoneTable; import forge.game.combat.Combat; import forge.game.phase.PhaseType; @@ -296,7 +297,7 @@ public abstract class SpellAbilityEffect { } delTrig.append("| TriggerDescription$ ").append(desc); - final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic); + final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), CardUtil.getLKICopy(sa.getHostCard()), intrinsic); for (final Card c : crds) { trig.addRemembered(c); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index a660dca451e..7d2d07e1985 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -502,7 +502,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // gameCard is LKI in that case, the card is not in game anymore // or the timestamp did change // this should check Self too - if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) { + if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard) || gameCard.isPhasedOut()) { continue; } if (sa.usesTargeting() && !gameCard.canBeTargetedBy(sa)) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java index cc4bc385c12..4c0f1995f72 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java @@ -72,7 +72,7 @@ public class PhasesEffect extends SpellAbilityEffect { for (final Card tgtC : tgtCards) { if (!tgtC.isPhasedOut()) { tgtC.phase(false); - if ( tgtC.isPhasedOut()) { + if (tgtC.isPhasedOut()) { tgtC.setWontPhaseInNormal(wontPhaseInNormal); } } 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 7aafdbd53cf..0c8cccc9be1 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -4609,8 +4609,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { setDirectlyPhasedOut(direct); } - if (hasCardAttachments()) { - for (final Card eq : getAttachedCards()) { + // CR 702.25g + if (!getAllAttachedCards().isEmpty()) { + for (final Card eq : getAllAttachedCards()) { if (eq.isPhasedOut() == phasingIn) { eq.phase(fromUntapStep, false); } @@ -4627,7 +4628,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } private boolean switchPhaseState(final boolean fromUntapStep) { - if (phasedOut && hasKeyword("CARDNAME can't phase in.")) { return false; } @@ -4646,6 +4646,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // If this is currently PhasedIn, it's about to phase out. // Run trigger before it does because triggers don't work with phased out objects getGame().getTriggerHandler().runTrigger(TriggerType.PhaseOut, runParams, false); + // when it doesn't exist the game will no longer see it as tapped + runUntapCommands(); + // TODO need to run UntilHostLeavesPlay commands but only when worded "for as long as" } setPhasedOut(!phasedOut); @@ -4659,6 +4662,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { getGame().getTriggerHandler().registerActiveTrigger(this, false); getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false); } + + game.updateLastStateForCard(this); return true; } @@ -5769,11 +5774,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final boolean canBeDestroyed() { - return isInPlay() && (!hasKeyword(Keyword.INDESTRUCTIBLE) || (isCreature() && getNetToughness() <= 0)); + return isInPlay() && !isPhasedOut() && (!hasKeyword(Keyword.INDESTRUCTIBLE) || (isCreature() && getNetToughness() <= 0)); } public final boolean canBeSacrificed() { - return isInPlay() && !this.isPhasedOut() && !hasKeyword("CARDNAME can't be sacrificed."); + return isInPlay() && !isPhasedOut() && !hasKeyword("CARDNAME can't be sacrificed."); } @Override 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 b477bcc6010..a6b61a78995 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -47,6 +47,11 @@ public class CardProperty { final Card lki = game.getChangeZoneLKIInfo(card); 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; + } + // by name can also have color names, so needs to happen before colors. if (property.startsWith("named")) { String name = TextUtil.fastReplace(property.substring(5), ";", ","); // for some legendary cards 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 1d57962e6ab..f6181cdf596 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -253,6 +253,8 @@ public final class CardUtil { newCopy.setCounters(Maps.newHashMap(in.getCounters())); newCopy.setColor(in.determineColor().getColor()); + newCopy.setPhasedOut(in.isPhasedOut()); + newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); newCopy.setDamageHistory(in.getDamageHistory()); for (Card c : in.getBlockedThisTurn()) { 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 3adc5e03416..9974a79bebf 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -475,7 +475,7 @@ public class PhaseHandler implements java.io.Serializable { boolean manaBurns = game.getRules().hasManaBurn() || (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manaBurn)); if (manaBurns) { - p.loseLife(burn,true); + p.loseLife(burn, true); } } @@ -485,7 +485,7 @@ public class PhaseHandler implements java.io.Serializable { break; case UPKEEP: - for (Card c : game.getCardsIn(ZoneType.Battlefield)) { + for (Card c : game.getCardsIncludePhasingIn(ZoneType.Battlefield)) { c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(playerTurn); c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(playerTurn); c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(playerTurn); 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 e5f2ffdc0e6..1fec36dc06b 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -71,6 +71,8 @@ public class Untap extends Phase { final Player turn = game.getPhaseHandler().getPlayerTurn(); Untap.doPhasing(turn); + + game.getAction().checkStaticAbilities(); doUntap(); } diff --git a/forge-gui/res/cardsfolder/a/azors_elocutors.txt b/forge-gui/res/cardsfolder/a/azors_elocutors.txt index bebbf501851..852b6471dee 100644 --- a/forge-gui/res/cardsfolder/a/azors_elocutors.txt +++ b/forge-gui/res/cardsfolder/a/azors_elocutors.txt @@ -8,5 +8,5 @@ SVar:WinGame:DB$ WinsGame | Defined$ You | ConditionCheckSVar$ TalkedEnough | Co SVar:TalkedEnough:Count$CardCounters.FILIBUSTER T:Mode$ DamageDone | ValidTarget$ You | TriggerZones$ Battlefield | Execute$ RemoveCounter | TriggerDescription$ Whenever a source deals damage to you, remove a filibuster counter from CARDNAME. SVar:RemoveCounter:DB$ RemoveCounter | Defined$ Self | CounterType$ FILIBUSTER | CounterNum$ 1 -SVar:Picture:http://www.wizards.com/global/images/magic/general/azors_elocutors.jpg +SVar:AIEvaluationModifier:30 Oracle:At the beginning of your upkeep, put a filibuster counter on Azor's Elocutors. Then if Azor's Elocutors has five or more filibuster counters on it, you win the game.\nWhenever a source deals damage to you, remove a filibuster counter from Azor's Elocutors.