diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 4902eebdcd9..2fe9bd55873 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1029,7 +1029,7 @@ public class PlayerControllerAi extends PlayerController { */ if (sa.isMayChooseNewTargets() && !sa.setupTargets()) { if (sa.isSpell()) { - sa.getHostCard().ceaseToExist(false); + player.getGame().getAction().ceaseToExist(sa.getHostCard(), false); } continue; } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 0527a1e0063..08da265ce41 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -785,12 +785,12 @@ public class Game { // unattach all "Enchant Player" c.removeAttachedTo(p); if (c.getOwner().equals(p)) { - for(Card cc : cards) { + for (Card cc : cards) { cc.removeImprintedCard(c); cc.removeEncodedCard(c); cc.removeRemembered(c); } - c.ceaseToExist(false); + getAction().ceaseToExist(c, false); // CR 603.2f owner of trigger source lost game triggerHandler.clearDelayedTrigger(c); } else { diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index edd56ddfa30..b4dcf95746b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -919,6 +919,38 @@ public class GameAction { } } + public void ceaseToExist(Card c, boolean skipTrig) { + final String origin = c.getZone().getZoneType().name(); + + c.getZone().remove(c); + c.setZone(null); + + // CR 603.6c other players LTB triggers should work + if (!skipTrig) { + game.addChangeZoneLKIInfo(c); + Card lki = null; + CardCollectionView lastBattlefield = game.getLastStateBattlefield(); + int idx = lastBattlefield.indexOf(c); + if (idx != -1) { + lki = lastBattlefield.get(idx); + } + if (lki == null) { + lki = CardUtil.getLKICopy(c); + } + if (game.getCombat() != null) { + game.getCombat().removeFromCombat(c); + game.getCombat().saveLKI(lki); + } + game.getTriggerHandler().registerActiveLTBTrigger(lki); + + final Map runParams = AbilityKey.mapFromCard(c); + runParams.put(AbilityKey.CardLKI, lki); + runParams.put(AbilityKey.Origin, origin); + game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, false); + game.getTriggerHandler().runWaitingTriggers(); + } + } + // Temporarily disable (if mode = true) actively checking static abilities. private void setHoldCheckingStaticAbilities(boolean mode) { holdCheckingStaticAbilities = mode; diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index eaae7e1bb70..194c66cfeb6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -3,6 +3,8 @@ package forge.game.ability.effects; import java.util.Arrays; import java.util.List; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import forge.GameCommand; @@ -13,12 +15,14 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardFactory; import forge.game.card.CardLists; +import forge.game.card.CardPredicates; import forge.game.event.GameEventCardStatsChanged; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.CardTranslation; import forge.util.Localizer; +import forge.util.collect.FCollection; public class CloneEffect extends SpellAbilityEffect { // TODO update this method @@ -139,10 +143,6 @@ public class CloneEffect extends SpellAbilityEffect { tgtCard.updateStateForView(); - //Clear Remembered and Imprint lists - tgtCard.clearRemembered(); - tgtCard.clearImprintedCards(); - // check if clone is now an Aura that needs to be attached if (tgtCard.isAura() && !tgtCard.isInZone(ZoneType.Battlefield)) { AttachEffect.attachAuraOnIndirectEnterBattlefield(tgtCard); @@ -150,12 +150,23 @@ public class CloneEffect extends SpellAbilityEffect { if (sa.hasParam("Duration")) { final Card cloneCard = tgtCard; + // if clone is temporary, target needs old values back after + final Iterable clonedImprinted = new CardCollection(tgtCard.getImprintedCards()); + final Iterable clonedRemembered = new FCollection<>(tgtCard.getRemembered()); + final GameCommand unclone = new GameCommand() { private static final long serialVersionUID = -78375985476256279L; @Override public void run() { if (cloneCard.removeCloneState(ts)) { + // remove values gained while being cloned + cloneCard.clearImprintedCards(); + cloneCard.clearRemembered(); + // restore original Remembered and Imprinted, ignore cards from players who lost + cloneCard.addImprintedCards(Iterables.filter(clonedImprinted, Predicates.not(CardPredicates.inZone(ZoneType.None)))); + cloneCard.addRemembered(Iterables.filter(clonedRemembered, Player.class)); + cloneCard.addRemembered(Iterables.filter(Iterables.filter(clonedRemembered, Card.class), CardPredicates.ownerLives())); cloneCard.updateStateForView(); game.fireEvent(new GameEventCardStatsChanged(cloneCard)); } @@ -176,9 +187,15 @@ public class CloneEffect extends SpellAbilityEffect { sa.getHostCard().addFacedownCommand(unclone); } } + + //Clear Remembered and Imprint lists + tgtCard.clearRemembered(); + tgtCard.clearImprintedCards(); + if (sa.hasParam("RememberCloneOrigin")) { tgtCard.addRemembered(cardToCopy); } + game.fireEvent(new GameEventCardStatsChanged(tgtCard)); } // cloneResolve 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 50d0f287809..00342d79ea4 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -6762,17 +6762,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } - public void ceaseToExist(boolean skipTrig) { - // CR 603.6c other players LTB triggers should work - if (skipTrig) { - getZone().remove(this); - setZone(getOwner().getZone(ZoneType.None)); - } - else { - game.getAction().moveTo(ZoneType.None, this, null); - } - } - public void forceTurnFaceUp() { getGame().getTriggerHandler().suppressMode(TriggerType.TurnFaceUp); turnFaceUp(false, false, null); diff --git a/forge-game/src/main/java/forge/game/card/CardPredicates.java b/forge-game/src/main/java/forge/game/card/CardPredicates.java index 56c22dd1429..8db9d7bb215 100644 --- a/forge-game/src/main/java/forge/game/card/CardPredicates.java +++ b/forge-game/src/main/java/forge/game/card/CardPredicates.java @@ -68,6 +68,15 @@ public final class CardPredicates { }; } + public static final Predicate ownerLives() { + return new Predicate() { + @Override + public boolean apply(final Card c) { + return !c.getOwner().hasLost(); + } + }; + } + public static final Predicate isType(final String cardType) { return new Predicate() { @Override diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index cd045d9e631..126ee4656d0 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -2357,7 +2357,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void rollback() { for (Card c : rollbackEffects) { - c.ceaseToExist(true); + c.getGame().getAction().ceaseToExist(c, true); } rollbackEffects.clear(); } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 656829d12b1..85c339d00ed 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -319,7 +319,9 @@ public class TriggerHandler { checkStatics |= type.equals("Battlefield"); } else { final ZoneType zone = (ZoneType) runParams.get(AbilityKey.Destination); - checkStatics |= zone.equals(ZoneType.Battlefield); + if (zone != null) { + checkStatics |= zone.equals(ZoneType.Battlefield); + } } } diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java index 434d854065a..18db18e90c2 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -586,7 +586,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable