mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Merge branch 'phasing' into 'master'
Phasing Fixes Closes #531 See merge request core-developers/forge!4808
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4609,8 +4609,9 @@ public class Card extends GameEntity implements Comparable<Card>, 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<Card>, 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<Card>, 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);
|
||||
@@ -4660,6 +4663,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.PhaseIn, runParams, false);
|
||||
}
|
||||
|
||||
game.updateLastStateForCard(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5769,11 +5774,11 @@ public class Card extends GameEntity implements Comparable<Card>, 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -72,6 +72,8 @@ public class Untap extends Phase {
|
||||
final Player turn = game.getPhaseHandler().getPlayerTurn();
|
||||
Untap.doPhasing(turn);
|
||||
|
||||
game.getAction().checkStaticAbilities();
|
||||
|
||||
doUntap();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user