From 007f9dc880cb1636ac18cc77df3272bde9a4af74 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sat, 11 Feb 2023 10:07:39 +0100 Subject: [PATCH 1/6] Flashback: use castKeyword --- .../src/main/java/forge/game/GameAction.java | 2 +- .../main/java/forge/game/GameActionUtil.java | 2 ++ .../java/forge/game/card/CardFactoryUtil.java | 2 +- .../forge/game/keyword/KeywordInstance.java | 19 ++++++++++++++++++- .../forge/game/keyword/KeywordInterface.java | 7 +++++-- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 0096337d519..3da5755451b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -619,7 +619,7 @@ public class GameAction { checkStaticAbilities(); // 400.7g try adding keyword back into card if it doesn't already have it - if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && c.equals(cause.getHostCard())) { + if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && !cause.isIntrinsic() && c.equals(cause.getHostCard())) { if (cause.getKeyword() != null) { if (!copied.getKeywords().contains(cause.getKeyword())) { copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getTimestamp(), 0, false); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 0eb493b2808..251392b53e7 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -224,6 +224,7 @@ public final class GameActionUtil { newSA.setAlternativeCost(AlternativeCost.Escape); newSA.getRestrictions().setZone(ZoneType.Graveyard); + newSA.setIntrinsic(inst.isIntrinsic()); alternatives.add(newSA); } else if (keyword.startsWith("Flashback")) { @@ -255,6 +256,7 @@ public final class GameActionUtil { flashback.setAlternativeCost(AlternativeCost.Flashback); flashback.getRestrictions().setZone(ZoneType.Graveyard); flashback.setKeyword(inst); + flashback.setIntrinsic(inst.isIntrinsic()); alternatives.add(flashback); } else if (keyword.startsWith("Foretell")) { // Foretell cast only from Exile diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 4d8de4af183..be741a402a0 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2285,7 +2285,7 @@ public class CardFactoryUtil { } else if (keyword.startsWith("Flashback")) { StringBuilder sb = new StringBuilder(); sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile "); - sb.append("| ValidStackSa$ Spell.Flashback | Description$ Flashback"); + sb.append("| ValidStackSa$ Spell.Flashback+castKeyword | Description$ Flashback"); if (keyword.contains(":")) { // K:Flashback:Cost:ExtraParams:ExtraDescription final String[] k = keyword.split(":"); diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java index 7b37656fb64..cd332dd7d2c 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java @@ -20,6 +20,9 @@ import io.sentry.Breadcrumb; import io.sentry.Sentry; public abstract class KeywordInstance> implements KeywordInterface { + private Card hostCard = null; + private boolean intrinsic = false; + private Keyword keyword; private String original; private long staticId = 0; @@ -87,6 +90,8 @@ public abstract class KeywordInstance> implements K * @see forge.game.keyword.KeywordInterface#createTraits(forge.game.card.Card, boolean, boolean) */ public final void createTraits(final Card host, final boolean intrinsic, final boolean clear) { + this.hostCard = host; + this.intrinsic = intrinsic; if (clear) { triggers.clear(); replacements.clear(); @@ -249,7 +254,7 @@ public abstract class KeywordInstance> implements K public KeywordInterface copy(final Card host, final boolean lki) { try { KeywordInstance result = (KeywordInstance) super.clone(); - + result.hostCard = host; result.abilities = Lists.newArrayList(); for (SpellAbility sa : this.abilities) { SpellAbility copy = sa.copy(host, lki); @@ -300,11 +305,17 @@ public abstract class KeywordInstance> implements K return !list.isEmpty() && keyword.isMultipleRedundant; } + @Override + public Card getHostCard() { + return hostCard; + } + /* (non-Javadoc) * @see forge.game.keyword.KeywordInterface#setHostCard(forge.game.card.Card) */ @Override public void setHostCard(Card host) { + this.hostCard = host; for (SpellAbility sa : this.abilities) { sa.setHostCard(host); } @@ -322,8 +333,14 @@ public abstract class KeywordInstance> implements K } } + @Override + public boolean isIntrinsic() { + return intrinsic; + } + @Override public void setIntrinsic(final boolean value) { + this.intrinsic = value; for (SpellAbility sa : this.abilities) { sa.setIntrinsic(value); } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java index 43620f225ca..3a3773aae3b 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java @@ -11,6 +11,11 @@ import forge.game.trigger.Trigger; public interface KeywordInterface extends Cloneable { + Card getHostCard(); + void setHostCard(final Card host); + boolean isIntrinsic(); + void setIntrinsic(final boolean value); + String getOriginal(); Keyword getKeyword(); @@ -34,8 +39,6 @@ public interface KeywordInterface extends Cloneable { void addSpellAbility(final SpellAbility s); void addStaticAbility(final StaticAbility st); - void setHostCard(final Card host); - void setIntrinsic(final boolean value); /** * @return the triggers From 3f7dc8db98ae9e2271bc7194069c45a8eb64e483 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 12 Feb 2023 13:09:30 +0100 Subject: [PATCH 2/6] Card: setChangedCardKeywords should not copy when adding to stack --- .../main/java/forge/ai/simulation/GameCopier.java | 2 +- forge-game/src/main/java/forge/game/GameAction.java | 6 ++++-- forge-game/src/main/java/forge/game/card/Card.java | 13 +++++++++++-- .../src/main/java/forge/game/card/CardUtil.java | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 206fffaa29b..e53b717fb7b 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -356,7 +356,7 @@ public class GameCopier { newCard.setChangedCardTypes(c.getChangedCardTypesTable()); newCard.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable()); - newCard.setChangedCardKeywords(c.getChangedCardKeywords()); + newCard.setChangedCardKeywords(c.getChangedCardKeywords(), true); newCard.setChangedCardNames(c.getChangedCardNames()); for (Table.Cell> kw : c.getHiddenExtrinsicKeywordsTable().cellSet()) { diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 3da5755451b..56f5ca612b2 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -268,7 +268,7 @@ public class GameAction { // when moving to stack, copy changed card information copied.setChangedCardColors(c.getChangedCardColorsTable()); copied.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable()); - copied.setChangedCardKeywords(c.getChangedCardKeywords()); + copied.setChangedCardKeywords(c.getChangedCardKeywords(), false); copied.setChangedCardTypes(c.getChangedCardTypesTable()); copied.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable()); copied.setChangedCardNames(c.getChangedCardNames()); @@ -622,7 +622,9 @@ public class GameAction { if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && !cause.isIntrinsic() && c.equals(cause.getHostCard())) { if (cause.getKeyword() != null) { if (!copied.getKeywords().contains(cause.getKeyword())) { - copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getTimestamp(), 0, false); + copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getNextTimestamp(), 0, false); + // update Keyword Cache + copied.updateKeywords(); } } } 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 6a7f9726588..27ede8ab54d 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -6876,10 +6876,19 @@ public class Card extends GameEntity implements Comparable, IHasSVars { this.changedCardTypesCharacterDefining.putAll(changedCardTypes); } - public void setChangedCardKeywords(Table changedCardKeywords) { + public void setChangedCardKeywords(Table changedCardKeywords, boolean copy) { this.changedCardKeywords.clear(); for (Table.Cell entry : changedCardKeywords.cellSet()) { - this.changedCardKeywords.put(entry.getRowKey(), entry.getColumnKey(), entry.getValue().copy(this, true)); + KeywordsChange result = entry.getValue(); + if (copy) { + result = result.copy(this, true); + } else { + // do not copy the keywords, just update the host + for (KeywordInterface k : result.getKeywords()) { + k.setHostCard(this); + } + } + this.changedCardKeywords.put(entry.getRowKey(), entry.getColumnKey(), result); } } 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 6b266177464..96ded79da40 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -287,7 +287,7 @@ public final class CardUtil { newCopy.setChangedCardColors(in.getChangedCardColorsTable()); newCopy.setChangedCardColorsCharacterDefining(in.getChangedCardColorsCharacterDefiningTable()); - newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); + newCopy.setChangedCardKeywords(in.getChangedCardKeywords(), true); newCopy.setChangedCardTypes(in.getChangedCardTypesTable()); newCopy.setChangedCardTypesCharacterDefining(in.getChangedCardTypesCharacterDefiningTable()); newCopy.setChangedCardNames(in.getChangedCardNames()); From 9d2ecfdeaab4b858311dbfe45d40c7c78bf8f914 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 12 Feb 2023 15:20:08 +0100 Subject: [PATCH 3/6] GameAction: Remove unneeded code --- .../src/main/java/forge/game/GameAction.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 56f5ca612b2..fbc30a873d4 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -265,25 +265,13 @@ public class GameAction { copied.setTimestamp(c.getTimestamp()); if (zoneTo.is(ZoneType.Stack)) { - // when moving to stack, copy changed card information - copied.setChangedCardColors(c.getChangedCardColorsTable()); - copied.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable()); - copied.setChangedCardKeywords(c.getChangedCardKeywords(), false); - copied.setChangedCardTypes(c.getChangedCardTypesTable()); - copied.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable()); - copied.setChangedCardNames(c.getChangedCardNames()); - copied.setChangedCardTraits(c.getChangedCardTraits()); - copied.setDrawnThisTurn(c.getDrawnThisTurn()); - - copied.copyChangedTextFrom(c); - - // clean up changes that come from its own static abilities - copied.cleanupCopiedChangesFrom(c); + // try not to copy changed stats when moving to stack // copy exiled properties when adding to stack // will be cleanup later in MagicStack copied.setExiledWith(c.getExiledWith()); copied.setExiledBy(c.getExiledBy()); + copied.setDrawnThisTurn(c.getDrawnThisTurn()); // copy bestow timestamp copied.setBestowTimestamp(c.getBestowTimestamp()); From a9d95d55ee7a0789cefc3f15873e53c8cbfbda6e Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 12 Feb 2023 15:21:53 +0100 Subject: [PATCH 4/6] Revert setChangedCardKeywords --- .../main/java/forge/ai/simulation/GameCopier.java | 2 +- forge-game/src/main/java/forge/game/card/Card.java | 13 ++----------- .../src/main/java/forge/game/card/CardUtil.java | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index e53b717fb7b..206fffaa29b 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -356,7 +356,7 @@ public class GameCopier { newCard.setChangedCardTypes(c.getChangedCardTypesTable()); newCard.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable()); - newCard.setChangedCardKeywords(c.getChangedCardKeywords(), true); + newCard.setChangedCardKeywords(c.getChangedCardKeywords()); newCard.setChangedCardNames(c.getChangedCardNames()); for (Table.Cell> kw : c.getHiddenExtrinsicKeywordsTable().cellSet()) { 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 27ede8ab54d..6a7f9726588 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -6876,19 +6876,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { this.changedCardTypesCharacterDefining.putAll(changedCardTypes); } - public void setChangedCardKeywords(Table changedCardKeywords, boolean copy) { + public void setChangedCardKeywords(Table changedCardKeywords) { this.changedCardKeywords.clear(); for (Table.Cell entry : changedCardKeywords.cellSet()) { - KeywordsChange result = entry.getValue(); - if (copy) { - result = result.copy(this, true); - } else { - // do not copy the keywords, just update the host - for (KeywordInterface k : result.getKeywords()) { - k.setHostCard(this); - } - } - this.changedCardKeywords.put(entry.getRowKey(), entry.getColumnKey(), result); + this.changedCardKeywords.put(entry.getRowKey(), entry.getColumnKey(), entry.getValue().copy(this, true)); } } 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 96ded79da40..6b266177464 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -287,7 +287,7 @@ public final class CardUtil { newCopy.setChangedCardColors(in.getChangedCardColorsTable()); newCopy.setChangedCardColorsCharacterDefining(in.getChangedCardColorsCharacterDefiningTable()); - newCopy.setChangedCardKeywords(in.getChangedCardKeywords(), true); + newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); newCopy.setChangedCardTypes(in.getChangedCardTypesTable()); newCopy.setChangedCardTypesCharacterDefining(in.getChangedCardTypesCharacterDefiningTable()); newCopy.setChangedCardNames(in.getChangedCardNames()); From 77dc1a9356deaf410dfd870f7591f7d4e2195635 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 12 Feb 2023 17:11:30 +0100 Subject: [PATCH 5/6] ~ Fix handle Bestow --- .../src/main/java/forge/game/GameAction.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index fbc30a873d4..26e7659c4c8 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -273,11 +273,13 @@ public class GameAction { copied.setExiledBy(c.getExiledBy()); copied.setDrawnThisTurn(c.getDrawnThisTurn()); - // copy bestow timestamp - copied.setBestowTimestamp(c.getBestowTimestamp()); if (cause != null && cause.isSpell() && c.equals(cause.getHostCard())) { copied.setCastSA(cause); + copied.setSplitStateToPlayAbility(cause); + + // CR 112.2 A spell’s controller is, by default, the player who put it on the stack. + copied.setController(cause.getActivatingPlayer(), 0); KeywordInterface kw = cause.getKeyword(); if (kw != null) { copied.addKeywordForStaticAbility(kw); @@ -864,17 +866,7 @@ public class GameAction { return moveToStack(c, cause, params); } public final Card moveToStack(final Card c, SpellAbility cause, Map params) { - Card result = moveTo(game.getStackZone(), c, cause, params); - if (cause != null && cause.isSpell() && result.equals(cause.getHostCard())) { - result.setSplitStateToPlayAbility(cause); - - // CR 112.2 A spell’s controller is, by default, the player who put it on the stack. - result.setController(cause.getActivatingPlayer(), 0); - // for triggers like from Wild-Magic Sorcerer - game.getAction().checkStaticAbilities(false); - game.getTriggerHandler().resetActiveTriggers(); - } - return result; + return moveTo(game.getStackZone(), c, cause, params); } public final Card moveToGraveyard(final Card c, SpellAbility cause) { From 4734b02c02fa07bc30e6378ad4699ce439bbeec3 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 12 Feb 2023 20:03:11 +0100 Subject: [PATCH 6/6] ReplacementHandler: found better way to Prevent StackOverflow for Riot --- .../src/main/java/forge/game/card/Card.java | 12 +++++ .../main/java/forge/game/card/CardUtil.java | 2 + .../game/replacement/ReplacementHandler.java | 53 +------------------ 3 files changed, 16 insertions(+), 51 deletions(-) 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 6a7f9726588..6de0ac1565e 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -20,6 +20,7 @@ package forge.game.card; import com.esotericsoftware.minlog.Log; import com.google.common.base.Predicates; import com.google.common.collect.*; + import forge.GameCommand; import forge.StaticData; import forge.card.*; @@ -4587,6 +4588,17 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } + public Table getStoredKeywords() { + return storedKeywords; + } + + public void setStoredKeywords(Table table, boolean lki) { + storedKeywords.clear(); + for (Table.Cell c : table.cellSet()) { + storedKeywords.put(c.getRowKey(), c.getColumnKey(), c.getValue().copy(this, lki)); + } + } + public final void addChangedCardKeywordsByText(final List keywords, final long timestamp, final long staticId, final boolean updateView) { // keywords should already created for Card, so no addKeywordsToCard // this one is done for Volrath's Shapeshifter which replaces all the card text 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 6b266177464..9fde3bf9fe3 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -293,6 +293,8 @@ public final class CardUtil { newCopy.setChangedCardNames(in.getChangedCardNames()); newCopy.setChangedCardTraits(in.getChangedCardTraits()); + newCopy.setStoredKeywords(in.getStoredKeywords(), true); + newCopy.copyChangedTextFrom(in); newCopy.setTimestamp(in.getTimestamp()); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index a5a1aefce5b..76a52eeae8e 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -31,7 +31,6 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.common.collect.Table; import forge.game.CardTraitBase; import forge.game.Game; @@ -48,10 +47,7 @@ import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardDamageMap; import forge.game.card.CardState; -import forge.game.card.CardTraitChanges; import forge.game.card.CardUtil; -import forge.game.keyword.KeywordInterface; -import forge.game.keyword.KeywordsChange; import forge.game.player.Player; import forge.game.player.PlayerCollection; import forge.game.spellability.AbilitySub; @@ -110,53 +106,6 @@ public class ReplacementHandler { game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList); checkAgain = true; - // need to check if Intrinsic has run - for (ReplacementEffect re : affectedLKI.getReplacementEffects()) { - if (re.isIntrinsic() && this.hasRun.contains(re)) { - re.setHasRun(true); - } - } - - // need to check non Intrinsic - for (Table.Cell e : affectedLKI.getChangedCardTraits().cellSet()) { - boolean hasRunRE = false; - String skey = String.valueOf(e.getRowKey()) + ":" + String.valueOf(e.getColumnKey()); - - for (ReplacementEffect re : this.hasRun) { - if (!re.isIntrinsic() && skey.equals(re.getSVar("_ReplacedTimestamp"))) { - hasRunRE = true; - break; - } - } - - for (ReplacementEffect re : e.getValue().getReplacements()) { - re.setSVar("_ReplacedTimestamp", skey); - if (hasRunRE) { - re.setHasRun(true); - } - } - } - for (Table.Cell e : affectedLKI.getChangedCardKeywords().cellSet()) { - boolean hasRunRE = false; - String skey = String.valueOf(e.getRowKey()) + ":" + String.valueOf(e.getColumnKey()); - - for (ReplacementEffect re : this.hasRun) { - if (!re.isIntrinsic() && skey.equals(re.getSVar("_ReplacedTimestamp"))) { - hasRunRE = true; - break; - } - } - - for (KeywordInterface k : e.getValue().getKeywords()) { - for (ReplacementEffect re : k.getReplacements()) { - re.setSVar("_ReplacedTimestamp", skey); - if (hasRunRE) { - re.setHasRun(true); - } - } - } - } - runParams.put(AbilityKey.Affected, affectedLKI); } @@ -222,6 +171,8 @@ public class ReplacementHandler { for (final ReplacementEffect re : affectedLKI.getReplacementEffects()) { re.setHostCard(affectedCard); } + // need to copy stored keywords from lki into real object to prevent the replacement effect from making new ones + affectedCard.setStoredKeywords(affectedLKI.getStoredKeywords(), true); runParams.put(AbilityKey.Affected, affectedCard); runParams.put(AbilityKey.NewCard, CardUtil.getLKICopy(affectedLKI)); }