From f32a8d7bb83d6c1eb06d58458c2df078ce499289 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sat, 5 Oct 2019 19:59:28 +0000 Subject: [PATCH] Card: CardTraits are now not added to Card/State anymore but uses timestamp --- .../src/main/java/forge/ai/ComputerUtil.java | 2 +- .../main/java/forge/ai/ComputerUtilCard.java | 4 +- .../main/java/forge/ai/ability/AnimateAi.java | 81 ++--- .../main/java/forge/game/CardTraitBase.java | 38 --- .../src/main/java/forge/game/GameAction.java | 10 +- .../main/java/forge/game/StaticEffect.java | 24 +- .../main/java/forge/game/StaticEffects.java | 9 + .../ability/effects/AnimateAllEffect.java | 80 +---- .../game/ability/effects/AnimateEffect.java | 123 ++------ .../ability/effects/AnimateEffectBase.java | 32 +- .../ability/effects/RegenerateBaseEffect.java | 2 +- .../ability/effects/RestartGameEffect.java | 1 - .../src/main/java/forge/game/card/Card.java | 186 ++++++++---- .../java/forge/game/card/CardFactoryUtil.java | 59 ---- .../forge/game/card/CardTraitChanges.java | 129 ++++++++ .../main/java/forge/game/card/CardUtil.java | 20 +- .../main/java/forge/game/player/Player.java | 2 +- .../game/replacement/ReplacementEffect.java | 1 - .../game/replacement/ReplacementHandler.java | 27 -- .../spellability/SpellAbilityPredicates.java | 9 + .../game/staticability/StaticAbility.java | 20 +- .../StaticAbilityContinuous.java | 277 ++++++++---------- .../staticability/StaticAbilityLayer.java | 9 +- .../main/java/forge/game/trigger/Trigger.java | 1 - .../forge/game/trigger/TriggerHandler.java | 39 --- 25 files changed, 487 insertions(+), 698 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/card/CardTraitChanges.java diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index f8550ba8066..fd96b4a1bfb 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1266,7 +1266,7 @@ public class ComputerUtil { public static boolean preventRunAwayActivations(final SpellAbility sa) { int activations = sa.getActivationsThisTurn(); - if (sa.isTemporary()) { + if (!sa.isIntrinsic()) { return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index a56e032dc87..2dbce19187d 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1128,14 +1128,14 @@ public class ComputerUtilCard { // assume it either benefits the player or disrupts the opponent for (final StaticAbility stAb : c.getStaticAbilities()) { final Map params = stAb.getMapParams(); - if (params.get("Mode").equals("Continuous") && stAb.isIntrinsic() && !stAb.isTemporary()) { + if (params.get("Mode").equals("Continuous") && stAb.isIntrinsic()) { priority = true; break; } } if (!priority) { for (final Trigger t : c.getTriggers()) { - if (t.isIntrinsic() && !t.isTemporary()) { + if (t.isIntrinsic()) { // has a triggered ability, could be benefitting the opponent or disrupting the AI priority = true; break; diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index 4f479d60678..baa0d00b7c5 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -467,26 +467,19 @@ public class AnimateAi extends SpellAbilityAi { AnimateEffectBase.doAnimate(card, sa, power, toughness, types, removeTypes, finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); - // back to duplicating AnimateEffect.resolve - // TODO will all these abilities/triggers/replacements/etc. lead to - // memory leaks or unintended effects? + // remove abilities final List removedAbilities = Lists.newArrayList(); - boolean clearAbilities = sa.hasParam("OverwriteAbilities"); boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean removeAll = sa.hasParam("RemoveAllAbilities"); boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities"); - if (clearAbilities || clearSpells || removeAll) { - for (final SpellAbility ab : card.getSpellAbilities()) { - if (removeAll - || (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility()) - || (ab.isAbility() && clearAbilities) - || (ab.isSpell() && clearSpells)) { - card.removeSpellAbility(ab); - removedAbilities.add(ab); - } - } + if (clearSpells) { + removedAbilities.addAll(Lists.newArrayList(card.getSpells())); + } + + if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) { + removedAbilities.add(sa); } // give abilities @@ -494,9 +487,7 @@ public class AnimateAi extends SpellAbilityAi { if (abilities.size() > 0) { for (final String s : abilities) { final String actualAbility = source.getSVar(s); - final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, source); - addedAbilities.add(grantedAbility); - card.addSpellAbility(grantedAbility); + addedAbilities.add(AbilityFactory.getAbility(actualAbility, card)); } } @@ -505,8 +496,8 @@ public class AnimateAi extends SpellAbilityAi { if (triggers.size() > 0) { for (final String s : triggers) { final String actualTrigger = source.getSVar(s); - final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, source, false); - addedTriggers.add(card.addTrigger(parsedTrigger)); + final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, card, false); + addedTriggers.add(parsedTrigger); } } @@ -515,24 +506,28 @@ public class AnimateAi extends SpellAbilityAi { if (replacements.size() > 0) { for (final String s : replacements) { final String actualReplacement = source.getSVar(s); - final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, - source, false); - addedReplacements.add(card.addReplacementEffect(parsedReplacement)); + final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, card, false); + addedReplacements.add(parsedReplacement); } } - // suppress triggers from the animated card - final List removedTriggers = Lists.newArrayList(); - if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) { - for (final Trigger trigger : card.getTriggers()) { - if (removeIntrinsic && !trigger.isIntrinsic()) { - continue; - } - trigger.setSuppressed(true); - removedTriggers.add(trigger); + // give static abilities (should only be used by cards to give + // itself a static ability) + final List addedStaticAbilities = Lists.newArrayList(); + if (stAbs.size() > 0) { + for (final String s : stAbs) { + final String actualAbility = source.getSVar(s); + addedStaticAbilities.add(new StaticAbility(actualAbility, card)); } } + if (removeAll || removeIntrinsic + || !addedAbilities.isEmpty() || !removedAbilities.isEmpty() || !addedTriggers.isEmpty() + || !addedReplacements.isEmpty() || !addedStaticAbilities.isEmpty()) { + card.addChangedCardTraits(addedAbilities, removedAbilities, addedTriggers, addedReplacements, + addedStaticAbilities, removeAll, false, removeIntrinsic, timestamp); + } + // give static abilities (should only be used by cards to give // itself a static ability) if (stAbs.size() > 0) { @@ -560,30 +555,6 @@ public class AnimateAi extends SpellAbilityAi { card.setSVar(name, actualsVar); } } - - // suppress static abilities from the animated card - final List removedStatics = Lists.newArrayList(); - if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) { - for (final StaticAbility stAb : card.getStaticAbilities()) { - if (removeIntrinsic && !stAb.isIntrinsic()) { - continue; - } - stAb.setTemporarilySuppressed(true); - removedStatics.add(stAb); - } - } - - // suppress static abilities from the animated card - final List removedReplacements = Lists.newArrayList(); - if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) { - for (final ReplacementEffect re : card.getReplacementEffects()) { - if (removeIntrinsic && !re.isIntrinsic()) { - continue; - } - re.setTemporarilySuppressed(true); - removedReplacements.add(re); - } - } ComputerUtilCard.applyStaticContPT(game, card, null); } diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index 1c72d72fe38..34c2a77edf5 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -38,15 +38,9 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { /** The is intrinsic. */ protected boolean intrinsic; - /** The temporary. */ - protected boolean temporary = false; - /** The suppressed. */ protected boolean suppressed = false; - /** The temporarily suppressed. */ - protected boolean temporarilySuppressed = false; - protected Map sVars = Maps.newHashMap(); protected Map intrinsicChangedTextColors = Maps.newHashMap(); @@ -66,24 +60,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { */ private static final ImmutableList noChangeKeys = ImmutableList.builder() .add("TokenScript", "LegacyImage", "TokenImage", "NewName").build(); - /** - * Sets the temporary. - * - * @param temp - * the new temporary - */ - public final void setTemporary(final boolean temp) { - this.temporary = temp; - } - - /** - * Checks if is temporary. - * - * @return true, if is temporary - */ - public final boolean isTemporary() { - return this.temporary; - } /** *

@@ -196,26 +172,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { this.suppressed = supp; } - /** - * Sets the temporarily suppressed. - * - * @param supp - * the new temporarily suppressed - */ - public final void setTemporarilySuppressed(final boolean supp) { - this.temporarilySuppressed = supp; - } - /** * Checks if is suppressed. * * @return true, if is suppressed */ public final boolean isSuppressed() { - return (this.suppressed || this.temporarilySuppressed); - } - - protected final boolean isNonTempSuppressed() { return this.suppressed; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 8df103f9c77..e7fb4887257 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -760,8 +760,6 @@ public class GameAction { // remove old effects game.getStaticEffects().clearStaticEffects(affectedCards); - game.getTriggerHandler().cleanUpTemporaryTriggers(); - game.getReplacementHandler().cleanUpTemporaryReplacements(); for (final Player p : game.getPlayers()) { if (!game.getStack().isFrozen()) { @@ -779,17 +777,11 @@ public class GameAction { public boolean visit(final Card c) { // need to get Card from preList if able final Card co = preList.get(c); - List toRemove = Lists.newArrayList(); for (StaticAbility stAb : co.getStaticAbilities()) { - if (stAb.isTemporary()) { - toRemove.add(stAb); - } else if (stAb.getParam("Mode").equals("Continuous")) { + if (stAb.getParam("Mode").equals("Continuous")) { staticAbilities.add(stAb); } } - for (StaticAbility stAb : toRemove) { - co.removeStaticAbility(stAb); - } if (!co.getStaticCommandList().isEmpty()) { staticList.add(co); } diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index d315c1d06e4..76cb288b920 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -23,8 +23,6 @@ import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardUtil; import forge.game.player.Player; -import forge.game.spellability.AbilityStatic; -import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; import java.util.ArrayList; @@ -236,11 +234,7 @@ public class StaticEffect { } if (hasParam("IgnoreEffectCost")) { - for (final SpellAbility s : getSource().getSpellAbilities()) { - if (s instanceof AbilityStatic && s.isTemporary()) { - getSource().removeSpellAbility(s); - } - } + getSource().removeChangedCardTraits(getTimestamp()); } // modify players @@ -273,22 +267,12 @@ public class StaticEffect { // the view is updated in GameAction#checkStaticAbilities to avoid flickering // remove keywords - // TODO regular keywords currently don't try to use keyword multiplier // (Although nothing uses it at this time) if (hasParam("AddKeyword") || hasParam("RemoveKeyword") || hasParam("RemoveAllAbilities")) { affectedCard.removeChangedCardKeywords(getTimestamp()); } - // remove abilities - if (hasParam("AddAbility") || hasParam("GainsAbilitiesOf")) { - for (final SpellAbility s : affectedCard.getSpellAbilities().threadSafeIterable()) { - if (s.isTemporary()) { - affectedCard.removeSpellAbility(s, false); - } - } - } - if (addHiddenKeywords != null) { for (final String k : addHiddenKeywords) { affectedCard.removeHiddenExtrinsicKeyword(k); @@ -296,8 +280,10 @@ public class StaticEffect { } // remove abilities - if (hasParam("RemoveAllAbilities") || hasParam("RemoveIntrinsicAbilities")) { - affectedCard.unSuppressCardTraits(); + if (hasParam("AddAbility") || hasParam("GainsAbilitiesOf") + || hasParam("AddTrigger") || hasParam("AddStaticAbility") || hasParam("AddReplacementEffects") + || hasParam("RemoveAllAbilities") || hasParam("RemoveIntrinsicAbilities")) { + affectedCard.removeChangedCardTraits(getTimestamp()); } // remove Types diff --git a/forge-game/src/main/java/forge/game/StaticEffects.java b/forge-game/src/main/java/forge/game/StaticEffects.java index 9bba9d9b0d9..af677db1a3f 100644 --- a/forge-game/src/main/java/forge/game/StaticEffects.java +++ b/forge-game/src/main/java/forge/game/StaticEffects.java @@ -80,4 +80,13 @@ public class StaticEffects { public Iterable getEffects() { return staticEffects.values(); } + + public boolean removeStaticEffect(final StaticAbility staticAbility) { + final StaticEffect currentEffect = staticEffects.remove(staticAbility); + if (currentEffect == null) { + return false; + } + currentEffect.remove(); + return true; + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java index 623a727ed55..9e23a7a7183 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java @@ -14,19 +14,15 @@ import forge.game.player.Player; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementHandler; import forge.game.spellability.SpellAbility; -import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; -import forge.util.collect.FCollectionView; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; -import com.google.common.collect.ImmutableList; - public class AnimateAllEffect extends AnimateEffectBase { @Override @@ -160,30 +156,16 @@ public class AnimateAllEffect extends AnimateEffectBase { final String actualAbility = host.getSVar(s); final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); addedAbilities.add(grantedAbility); - c.addSpellAbility(grantedAbility); } } - // remove abilities - final List removedAbilities = new ArrayList<>(); - if (sa.hasParam("OverwriteAbilities") || removeAll || removeIntrinsic) { - for (final SpellAbility ab : c.getSpellAbilities()) { - if (ab.isAbility()) { - if (removeAll - || (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())) { - ab.setTemporarilySuppressed(true); - removedAbilities.add(ab); - } - } - } - } // give replacement effects final List addedReplacements = new ArrayList<>(); if (replacements.size() > 0) { for (final String s : replacements) { final String actualReplacement = host.getSVar(s); final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false); - addedReplacements.add(c.addReplacementEffect(parsedReplacement)); + addedReplacements.add(parsedReplacement); } } // Grant triggers @@ -192,45 +174,12 @@ public class AnimateAllEffect extends AnimateEffectBase { for (final String s : triggers) { final String actualTrigger = host.getSVar(s); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false); - addedTriggers.add(c.addTrigger(parsedTrigger)); + addedTriggers.add(parsedTrigger); } } - // suppress triggers from the animated card - final List removedTriggers = new ArrayList<>(); - if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) { - final FCollectionView triggersToRemove = c.getTriggers(); - for (final Trigger trigger : triggersToRemove) { - if (removeIntrinsic && !trigger.isIntrinsic()) { - continue; - } - trigger.setSuppressed(true); // why this not TemporarilySuppressed? - removedTriggers.add(trigger); - } - } - - // suppress static abilities from the animated card - final List removedStatics = new ArrayList<>(); - if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) { - for (final StaticAbility stAb : c.getStaticAbilities()) { - if (removeIntrinsic && !stAb.isIntrinsic()) { - continue; - } - stAb.setTemporarilySuppressed(true); - removedStatics.add(stAb); - } - } - - // suppress static abilities from the animated card - final List removedReplacements = new ArrayList<>(); - if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) { - for (final ReplacementEffect re : c.getReplacementEffects()) { - if (removeIntrinsic && !re.isIntrinsic()) { - continue; - } - re.setTemporarilySuppressed(true); - removedReplacements.add(re); - } + if (!addedAbilities.isEmpty() || !addedTriggers.isEmpty() || !addedReplacements.isEmpty()) { + c.addChangedCardTraits(addedAbilities, null, addedTriggers, addedReplacements, null, removeAll, false, removeIntrinsic, timestamp); } // give sVars @@ -247,27 +196,8 @@ public class AnimateAllEffect extends AnimateEffectBase { @Override public void run() { - doUnanimate(c, sa, finalDesc, hiddenKeywords, - addedAbilities, addedTriggers, addedReplacements, - ImmutableList.of(), timestamp); + doUnanimate(c, sa, hiddenKeywords, timestamp); - for (final SpellAbility sa : removedAbilities) { - sa.setTemporarilySuppressed(false); - } - // give back suppressed triggers - for (final Trigger t : removedTriggers) { - t.setSuppressed(false); - } - - // give back suppressed static abilities - for (final StaticAbility s : removedStatics) { - s.setTemporarilySuppressed(false); - } - - // give back suppressed replacement effects - for (final ReplacementEffect re : removedReplacements) { - re.setTemporarilySuppressed(false); - } game.fireEvent(new GameEventCardStatsChanged(c)); } }; diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java index 7f0fa438ebc..b319e84f37f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java @@ -162,25 +162,15 @@ public class AnimateEffect extends AnimateEffectBase { // remove abilities final List removedAbilities = Lists.newArrayList(); - boolean clearAbilities = sa.hasParam("OverwriteAbilities"); boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean removeAll = sa.hasParam("RemoveAllAbilities"); boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities"); - if (clearAbilities || clearSpells || removeAll) { - for (final SpellAbility ab : c.getSpellAbilities()) { - if (removeAll - || (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility()) - || (ab.isAbility() && clearAbilities) - || (ab.isSpell() && clearSpells)) { - ab.setTemporarilySuppressed(true); - removedAbilities.add(ab); - } - } + if (clearSpells) { + removedAbilities.addAll(Lists.newArrayList(c.getSpells())); } if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) { - c.removeSpellAbility(sa); removedAbilities.add(sa); } @@ -189,9 +179,7 @@ public class AnimateEffect extends AnimateEffectBase { if (abilities.size() > 0) { for (final String s : abilities) { final String actualAbility = source.getSVar(s); - final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); - addedAbilities.add(grantedAbility); - c.addSpellAbility(grantedAbility); + addedAbilities.add(AbilityFactory.getAbility(actualAbility, c)); } } @@ -201,7 +189,7 @@ public class AnimateEffect extends AnimateEffectBase { for (final String s : triggers) { final String actualTrigger = source.getSVar(s); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false); - addedTriggers.add(c.addTrigger(parsedTrigger)); + addedTriggers.add(parsedTrigger); } } @@ -211,19 +199,7 @@ public class AnimateEffect extends AnimateEffectBase { for (final String s : replacements) { final String actualReplacement = source.getSVar(s); final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false); - addedReplacements.add(c.addReplacementEffect(parsedReplacement)); - } - } - - // suppress triggers from the animated card - final List removedTriggers = Lists.newArrayList(); - if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) { - for (final Trigger trigger : c.getTriggers()) { - if (removeIntrinsic && !trigger.isIntrinsic()) { - continue; - } - trigger.setSuppressed(true); // why this not TemporarilySuppressed? - removedTriggers.add(trigger); + addedReplacements.add(parsedReplacement); } } @@ -233,7 +209,7 @@ public class AnimateEffect extends AnimateEffectBase { if (stAbs.size() > 0) { for (final String s : stAbs) { final String actualAbility = source.getSVar(s); - addedStaticAbilities.add(c.addStaticAbility(actualAbility)); + addedStaticAbilities.add(new StaticAbility(actualAbility, c)); } } @@ -251,30 +227,6 @@ public class AnimateEffect extends AnimateEffectBase { } } - // suppress static abilities from the animated card - final List removedStatics = Lists.newArrayList(); - if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) { - for (final StaticAbility stAb : c.getStaticAbilities()) { - if (removeIntrinsic && !stAb.isIntrinsic()) { - continue; - } - stAb.setTemporarilySuppressed(true); - removedStatics.add(stAb); - } - } - - // suppress static abilities from the animated card - final List removedReplacements = Lists.newArrayList(); - if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) { - for (final ReplacementEffect re : c.getReplacementEffects()) { - if (removeIntrinsic && !re.isIntrinsic()) { - continue; - } - re.setTemporarilySuppressed(true); - removedReplacements.add(re); - } - } - // give Remembered if (animateRemembered != null) { for (final Object o : AbilityUtils.getDefinedObjects(source, animateRemembered, sa)) { @@ -287,35 +239,40 @@ public class AnimateEffect extends AnimateEffectBase { @Override public void run() { - doUnanimate(c, sa, finalDesc, hiddenKeywords, - addedAbilities, addedTriggers, addedReplacements, - addedStaticAbilities, timestamp); + doUnanimate(c, sa, hiddenKeywords, timestamp); c.removeChangedName(timestamp); c.updateStateForView(); game.fireEvent(new GameEventCardStatsChanged(c)); - - for (final SpellAbility sa : removedAbilities) { - sa.setTemporarilySuppressed(false); - } - // give back suppressed triggers - for (final Trigger t : removedTriggers) { - t.setSuppressed(false); - } - - // give back suppressed static abilities - for (final StaticAbility s : removedStatics) { - s.setTemporarilySuppressed(false); - } - - // give back suppressed replacement effects - for (final ReplacementEffect re : removedReplacements) { - re.setTemporarilySuppressed(false); - } } }; + + if (sa.hasParam("RevertCost")) { + final ManaCost cost = new ManaCost(new ManaCostParser(sa.getParam("RevertCost"))); + final String desc = this.getStackDescription(sa); + final SpellAbility revertSA = new AbilityStatic(c, cost) { + @Override + public void resolve() { + unanimate.run(); + } + @Override + public String getDescription() { + return cost + ": End Effect: " + desc; + } + }; + addedAbilities.add(revertSA); + } + + // after unanimate to add RevertCost + if (removeAll || removeIntrinsic + || !addedAbilities.isEmpty() || !removedAbilities.isEmpty() || !addedTriggers.isEmpty() + || !addedReplacements.isEmpty() || !addedStaticAbilities.isEmpty()) { + c.addChangedCardTraits(addedAbilities, removedAbilities, addedTriggers, addedReplacements, + addedStaticAbilities, removeAll, false, removeIntrinsic, timestamp); + } + if (!permanent) { if (sa.hasParam("UntilEndOfCombat")) { game.getEndOfCombat().addUntil(unanimate); @@ -343,22 +300,6 @@ public class AnimateEffect extends AnimateEffectBase { } } - if (sa.hasParam("RevertCost")) { - final ManaCost cost = new ManaCost(new ManaCostParser(sa.getParam("RevertCost"))); - final String desc = this.getStackDescription(sa); - final SpellAbility revertSA = new AbilityStatic(c, cost) { - @Override - public void resolve() { - unanimate.run(); - c.removeSpellAbility(this); - } - @Override - public String getDescription() { - return cost + ": End Effect: " + desc; - } - }; - c.addSpellAbility(revertSA); - } game.fireEvent(new GameEventCardStatsChanged(c)); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java index 41399ba8a1f..7e3311e1f40 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java @@ -20,10 +20,8 @@ package forge.game.ability.effects; import forge.card.CardType; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.replacement.ReplacementEffect; + import forge.game.spellability.SpellAbility; -import forge.game.staticability.StaticAbility; -import forge.game.trigger.Trigger; import java.util.List; public abstract class AnimateEffectBase extends SpellAbilityEffect { @@ -128,14 +126,8 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { * @param timestamp * a long. */ - static void doUnanimate(final Card c, SpellAbility sa, final String colorDesc, - final List hiddenKeywords, final List addedAbilities, - final List addedTriggers, final List addedReplacements, - final List addedStaticAbilities, final long timestamp) { - - if (sa.hasParam("LastsIndefinitely")) { - return; - } + static void doUnanimate(final Card c, SpellAbility sa, + final List hiddenKeywords, final long timestamp) { c.removeNewPT(timestamp); @@ -144,26 +136,12 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { c.removeChangedCardTypes(timestamp); c.removeColor(timestamp); + c.removeChangedCardTraits(timestamp); + for (final String k : hiddenKeywords) { c.removeHiddenExtrinsicKeyword(k); } - for (final SpellAbility saAdd : addedAbilities) { - c.removeSpellAbility(saAdd); - } - - for (final Trigger t : addedTriggers) { - c.removeTrigger(t); - } - - for (final ReplacementEffect rep : addedReplacements) { - c.removeReplacementEffect(rep); - } - - for (final StaticAbility stAb : addedStaticAbilities) { - c.removeStaticAbility(stAb); - } - // any other unanimate cleanup if (!c.isCreature()) { c.unEquipAllCards(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java index d4ff911c8ef..2429a660154 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java @@ -58,7 +58,7 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect { + " | TriggerDescription$ " + trigSA.getDescription(); final Trigger trigger = TriggerHandler.parseTrigger(trigStr, eff, true); trigger.setOverridingAbility(trigSA); - eff.moveTrigger(trigger); + eff.addTrigger(trigger); } // Copy text changes diff --git a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java index 61848eba1f3..38275f3f961 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java @@ -55,7 +55,6 @@ public class RestartGameEffect extends SpellAbilityEffect { forge.game.trigger.Trigger.resetIDs(); TriggerHandler trigHandler = game.getTriggerHandler(); trigHandler.clearDelayedTrigger(); - trigHandler.cleanUpTemporaryTriggers(); trigHandler.suppressMode(TriggerType.ChangesZone); game.getStack().reset(); 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 a24259b01bc..c580357e6c8 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -18,6 +18,7 @@ package forge.game.card; import com.esotericsoftware.minlog.Log; +import com.google.common.base.Predicates; import com.google.common.base.Strings; import com.google.common.collect.*; import forge.GameCommand; @@ -121,6 +122,7 @@ public class Card extends GameEntity implements Comparable { private final Map changedCardTypes = Maps.newTreeMap(); private final NavigableMap changedCardNames = Maps.newTreeMap(); private final Map changedCardKeywords = Maps.newTreeMap(); + private final Map changedCardTraits = Maps.newTreeMap(); private final Map changedCardColors = Maps.newTreeMap(); private final NavigableMap clonedStates = Maps.newTreeMap(); private final NavigableMap textChangeStates = Maps.newTreeMap(); @@ -1012,28 +1014,15 @@ public class Card extends GameEntity implements Comparable { public final FCollectionView getTriggers() { return currentState.getTriggers(); } - // only used for LKI - public final void setTriggers(final Iterable trigs, boolean intrinsicOnly) { - final FCollection copyList = new FCollection<>(); - for (final Trigger t : trigs) { - if (!intrinsicOnly || t.isIntrinsic()) { - copyList.add(t.copy(this, true)); - } - } - currentState.setTriggers(copyList); - } public final Trigger addTrigger(final Trigger t) { - final Trigger newtrig = t.copy(this, false); - currentState.addTrigger(newtrig); - return newtrig; - } - public final void moveTrigger(final Trigger t) { - t.setHostCard(this); currentState.addTrigger(t); + return t; } + @Deprecated public final void removeTrigger(final Trigger t) { currentState.removeTrigger(t); } + @Deprecated public final void removeTrigger(final Trigger t, final CardStateName state) { getState(state).removeTrigger(t); } @@ -1050,6 +1039,25 @@ public class Card extends GameEntity implements Comparable { } public void updateTriggers(List list, CardState state) { + + boolean removeIntrinsic = false; + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveIntrinsic()) { + removeIntrinsic = true; + break; + } + } + if (removeIntrinsic) { + list.clear(); + } + + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveAll()) { + list.clear(); + } + list.addAll(ck.getTriggers()); + } + for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getTriggers()); } @@ -2379,10 +2387,12 @@ public class Card extends GameEntity implements Comparable { } } + @Deprecated public final void removeSpellAbility(final SpellAbility a) { removeSpellAbility(a, true); } + @Deprecated public final void removeSpellAbility(final SpellAbility a, final boolean updateView) { if (currentState.removeSpellAbility(a) && updateView) { currentState.getView().updateAbilityText(this, currentState); @@ -2408,15 +2418,44 @@ public class Card extends GameEntity implements Comparable { } public void updateSpellAbilities(List list, CardState state, Boolean mana) { + + boolean removeIntrinsic = false; + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveIntrinsic()) { + removeIntrinsic = true; + break; + } + } + if (removeIntrinsic) { + list.clear(); + } + // do Basic Land Abilities there - if (mana == null || mana == true) { + if (null == mana || true == mana) { updateBasicLandAbilities(list, state); } + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveNonMana()) { + // List only has nonMana + if (null == mana) { + List toRemove = Lists.newArrayList( + Iterables.filter(list, Predicates.not(SpellAbilityPredicates.isManaAbility()))); + list.removeAll(toRemove); + } else if (false == mana) { + list.clear(); + } + } else if (ck.isRemoveAll()) { + list.clear(); + } + list.removeAll(ck.getRemovedAbilities()); + list.addAll(ck.getAbilities()); + } + // add Facedown abilities from Original state but only if this state is face down // need CardStateView#getState or might crash in StackOverflow if (isInZone(ZoneType.Battlefield)) { - if ((mana == null || mana == false) && isFaceDown() && state.getView().getState() == CardStateName.FaceDown) { + if ((null == mana || false == mana) && isFaceDown() && state.getView().getState() == CardStateName.FaceDown) { for (SpellAbility sa : getState(CardStateName.Original).getNonManaAbilities()) { if (sa.isManifestUp() || sa.isMorphUp()) { list.add(sa); @@ -2729,14 +2768,14 @@ public class Card extends GameEntity implements Comparable { * * Control, Controller: "Control" is the system that determines who gets to use an object in the game. * An object's "controller" is the player who currently controls it. See rule 108.4. - * - * 400.6. If an object would move from one zone to another, determine what event is moving the object. - * If the object is moving to a public zone and its owner will be able to look at it in that zone, - * its owner looks at it to see if it has any abilities that would affect the move. - * If the object is moving to the battlefield, each other player who will be able to look at it in that - * zone does so. Then any appropriate replacement effects, whether they come from that object or from - * elsewhere, are applied to that event. If any effects or rules try to do two or more contradictory or - * mutually exclusive things to a particular object, that object’s CONTROLLER—or its OWNER + * + * 400.6. If an object would move from one zone to another, determine what event is moving the object. + * If the object is moving to a public zone and its owner will be able to look at it in that zone, + * its owner looks at it to see if it has any abilities that would affect the move. + * If the object is moving to the battlefield, each other player who will be able to look at it in that + * zone does so. Then any appropriate replacement effects, whether they come from that object or from + * elsewhere, are applied to that event. If any effects or rules try to do two or more contradictory or + * mutually exclusive things to a particular object, that object’s CONTROLLER—or its OWNER * IF IT HAS NO CONTROLLER—chooses which effect to apply, and what that effect does. */ if (controller != null) { @@ -3589,6 +3628,31 @@ public class Card extends GameEntity implements Comparable { getGame().fireEvent(new GameEventCardTapped(this, false)); } + public final void addChangedCardTraits(Collection spells, Collection removedAbilities, + Collection trigger, Collection replacements, Collection statics, + boolean removeAll, boolean removeNonMana, boolean removeIntrinsic, long timestamp) { + changedCardTraits.put(timestamp, new CardTraitChanges( + spells, removedAbilities, trigger, replacements, statics, removeAll, removeNonMana, removeIntrinsic + )); + // update view + updateAbilityTextForView(); + } + + public final boolean removeChangedCardTraits(long timestamp) { + return changedCardTraits.remove(timestamp) != null; + } + + public final Map getChangedCardTraits() { + return changedCardTraits; + } + + public final void setChangedCardTraits(Map changes) { + changedCardTraits.clear(); + for (Entry e : changes.entrySet()) { + changedCardTraits.put(e.getKey(), e.getValue().copy(this, true)); + } + } + // keywords are like flying, fear, first strike, etc... public final List getKeywords() { return getKeywords(currentState); @@ -4018,11 +4082,31 @@ public class Card extends GameEntity implements Comparable { currentState.addStaticAbility(stAb); return stAb; } + + @Deprecated public final void removeStaticAbility(StaticAbility stAb) { currentState.removeStaticAbility(stAb); } public void updateStaticAbilities(List list, CardState state) { + boolean removeIntrinsic = false; + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveIntrinsic()) { + removeIntrinsic = true; + break; + } + } + if (removeIntrinsic) { + list.clear(); + } + + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveAll()) { + list.clear(); + } + list.addAll(ck.getStaticAbilities()); + } + for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getStaticAbilities()); } @@ -5641,24 +5725,35 @@ public class Card extends GameEntity implements Comparable { return currentState.getReplacementEffects(); } - public void setReplacementEffects(final Iterable res) { - currentState.clearReplacementEffects(); - for (final ReplacementEffect replacementEffect : res) { - if (replacementEffect.isIntrinsic()) { - addReplacementEffect(replacementEffect.copy(this, false)); - } - } - } - public ReplacementEffect addReplacementEffect(final ReplacementEffect replacementEffect) { currentState.addReplacementEffect(replacementEffect); return replacementEffect; } + + @Deprecated public void removeReplacementEffect(ReplacementEffect replacementEffect) { currentState.removeReplacementEffect(replacementEffect); } public void updateReplacementEffects(List list, CardState state) { + + boolean removeIntrinsic = false; + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveIntrinsic()) { + removeIntrinsic = true; + break; + } + } + if (removeIntrinsic) { + list.clear(); + } + + for (final CardTraitChanges ck : changedCardTraits.values()) { + if (ck.isRemoveAll()) { + list.clear(); + } + list.addAll(ck.getReplacements()); + } for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getReplacements()); } @@ -6296,27 +6391,6 @@ public class Card extends GameEntity implements Comparable { withFlash.removeAll(timestamp); } - public void unSuppressCardTraits() { - // specially reset basic land abilities - for (final SpellAbility ab : basicLandAbilities) { - if (ab != null) { - ab.setTemporarilySuppressed(false); - } - } - for (final SpellAbility ab : getSpellAbilities()) { - ab.setTemporarilySuppressed(false); - } - for (final Trigger t : getTriggers()) { - t.setTemporarilySuppressed(false); - } - for (final StaticAbility stA : getStaticAbilities()) { - stA.setTemporarilySuppressed(false); - } - for (final ReplacementEffect rE : getReplacementEffects()) { - rE.setTemporarilySuppressed(false); - } - } - public boolean canBeDiscardedBy(SpellAbility sa) { if (!isInZone(ZoneType.Hand)) { return false; 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 8fc4114e6bf..f0317647e70 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -3758,7 +3758,6 @@ public class CardFactoryUtil { newSA.setDescription(sa.getDescription() + " (by paying " + cost.toSimpleString() + " instead of its mana cost)"); newSA.setIntrinsic(intrinsic); - newSA.setTemporary(intrinsic); inst.addSpellAbility(newSA); } @@ -3788,8 +3787,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.equals("Aftermath") && card.getCurrentStateName().equals(CardStateName.RightSplit)) { // Aftermath does modify existing SA, and does not add new one @@ -3809,8 +3806,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Awaken")) { final String[] k = keyword.split(":"); @@ -3835,8 +3830,6 @@ public class CardFactoryUtil { awakenSpell.setBasicSpell(false); awakenSpell.setPayCosts(awakenCost); awakenSpell.setIntrinsic(intrinsic); - - awakenSpell.setTemporary(!intrinsic); inst.addSpellAbility(awakenSpell); } else if (keyword.startsWith("Bestow")) { final String[] params = keyword.split(":"); @@ -3854,8 +3847,6 @@ public class CardFactoryUtil { sa.setStackDescription("Bestow - " + card.getName()); sa.setBasicSpell(false); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Dash")) { final String[] k = keyword.split(":"); @@ -3865,8 +3856,6 @@ public class CardFactoryUtil { final SpellAbility newSA = AbilityFactory.getAbility(dashString, card); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Emerge")) { final String[] kw = keyword.split(":"); @@ -3882,8 +3871,6 @@ public class CardFactoryUtil { newSA.setPayCosts(new Cost(costStr, false)); newSA.setDescription(sa.getDescription() + " (Emerge)"); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Embalm")) { final String[] kw = keyword.split(":"); @@ -3896,8 +3883,6 @@ public class CardFactoryUtil { "| SpellDescription$ (" + inst.getReminderText() + ")" ; final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.equals("Epic")) { // Epic does modify existing SA, and does not add new one @@ -3952,7 +3937,6 @@ public class CardFactoryUtil { // instantiate attach ability final SpellAbility newSA = AbilityFactory.getAbility(abilityStr.toString(), card); newSA.setIntrinsic(intrinsic); - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Eternalize")) { final String[] kw = keyword.split(":"); @@ -3982,8 +3966,6 @@ public class CardFactoryUtil { .append("| SpellDescription$ (").append(inst.getReminderText()).append(")"); final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Evoke")) { final String[] k = keyword.split(":"); @@ -4005,8 +3987,6 @@ public class CardFactoryUtil { newSA.setBasicSpell(false); newSA.setEvoke(true); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Fortify")) { String[] k = keyword.split(":"); @@ -4027,14 +4007,10 @@ public class CardFactoryUtil { // instantiate attach ability final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Fuse") && card.getCurrentStateName().equals(CardStateName.Original)) { final SpellAbility sa = AbilityFactory.buildFusedAbility(card); card.addSpellAbility(sa); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Haunt")) { if (!card.isCreature() && intrinsic) { @@ -4067,8 +4043,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Monstrosity")) { final String[] k = keyword.split(":"); @@ -4103,8 +4077,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Morph")) { @@ -4146,8 +4118,6 @@ public class CardFactoryUtil { SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); // extra secondary effect for Commander Ninjutsu @@ -4161,8 +4131,6 @@ public class CardFactoryUtil { sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } } else if (keyword.startsWith("Outlast")) { @@ -4187,8 +4155,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Prowl")) { @@ -4212,7 +4178,6 @@ public class CardFactoryUtil { newSA.setProwl(true); newSA.setIntrinsic(intrinsic); - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Reinforce")) { final String[] k = keyword.split(":"); @@ -4234,8 +4199,6 @@ public class CardFactoryUtil { if (n.equals("X")) { sa.setSVar("X", "Count$xPaid"); } - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Scavenge")) { final String[] k = keyword.split(":"); @@ -4250,8 +4213,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setSVar("ScavengeX", "Count$CardPower"); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Spectacle")) { @@ -4267,8 +4228,6 @@ public class CardFactoryUtil { newSA.setDescription(desc); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Surge")) { @@ -4284,8 +4243,6 @@ public class CardFactoryUtil { newSA.setDescription(desc); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Suspend") && !keyword.equals("Suspend")) { @@ -4336,8 +4293,6 @@ public class CardFactoryUtil { suspend.setStackDescription(sbStack.toString()); suspend.getRestrictions().setZone(ZoneType.Hand); - - suspend.setTemporary(!intrinsic); inst.addSpellAbility(suspend); } else if (keyword.startsWith("Transfigure")) { final String[] k = keyword.split(":"); @@ -4351,8 +4306,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setSVar("TransfigureX", "Count$CardManaCost"); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Transmute")) { final String[] k = keyword.split(":"); @@ -4367,8 +4320,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setSVar("TransmuteX", "Count$CardManaCost"); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Unearth")) { final String[] k = keyword.split(":"); @@ -4383,8 +4334,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.endsWith(" offering")) { @@ -4403,8 +4352,6 @@ public class CardFactoryUtil { newSA.setPayCosts(sa.getPayCosts()); newSA.setDescription(sa.getDescription() + " (" + offeringType + " offering)"); newSA.setIntrinsic(intrinsic); - - newSA.setTemporary(!intrinsic); inst.addSpellAbility(newSA); } else if (keyword.startsWith("Crew")) { @@ -4420,8 +4367,6 @@ public class CardFactoryUtil { final SpellAbility sa = AbilityFactory.getAbility(effect, card); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("Cycling")) { @@ -4440,8 +4385,6 @@ public class CardFactoryUtil { SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); sa.setIsCycling(true); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } else if (keyword.startsWith("TypeCycling")) { @@ -4466,8 +4409,6 @@ public class CardFactoryUtil { SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); sa.setIsCycling(true); sa.setIntrinsic(intrinsic); - - sa.setTemporary(!intrinsic); inst.addSpellAbility(sa); } diff --git a/forge-game/src/main/java/forge/game/card/CardTraitChanges.java b/forge-game/src/main/java/forge/game/card/CardTraitChanges.java new file mode 100644 index 00000000000..cb656beaaff --- /dev/null +++ b/forge-game/src/main/java/forge/game/card/CardTraitChanges.java @@ -0,0 +1,129 @@ +package forge.game.card; + +import java.util.Collection; +import java.util.List; + +import com.google.common.collect.Lists; + +import forge.game.replacement.ReplacementEffect; +import forge.game.spellability.SpellAbility; +import forge.game.staticability.StaticAbility; +import forge.game.trigger.Trigger; + +public class CardTraitChanges implements Cloneable { + + private List triggers = Lists.newArrayList(); + private List replacements = Lists.newArrayList(); + private List abilities = Lists.newArrayList(); + private List staticAbilities = Lists.newArrayList(); + + private List removedAbilities = Lists.newArrayList(); + + private boolean removeAll; + private boolean removeNonMana; + private boolean removeIntrinsic; + + public CardTraitChanges(Collection spells, Collection removedAbilities, + Collection trigger, Collection res, Collection st, + boolean removeAll, boolean removeNonMana, boolean removeIntrinsic) { + if (spells != null) { + this.abilities.addAll(spells); + } + if (removedAbilities != null) { + this.removedAbilities.addAll(removedAbilities); + } + if (trigger != null) { + this.triggers.addAll(trigger); + } + if (res != null) { + this.replacements.addAll(res); + } + if (st != null) { + this.staticAbilities.addAll(st); + } + + this.removeAll = removeAll; + this.removeNonMana = removeNonMana; + this.removeIntrinsic = removeIntrinsic; + } + + /** + * @return the triggers + */ + public Collection getTriggers() { + return triggers; + } + /** + * @return the replacements + */ + public Collection getReplacements() { + return replacements; + } + + /** + * @return the abilities + */ + public Collection getAbilities() { + return abilities; + } + + /** + * @return the abilities + */ + public Collection getRemovedAbilities() { + return removedAbilities; + } + + /** + * @return the staticAbilities + */ + public Collection getStaticAbilities() { + return staticAbilities; + } + + public boolean isRemoveAll() { + return removeAll; + } + + public boolean isRemoveNonMana() { + return removeNonMana; + } + + public boolean isRemoveIntrinsic() { + return removeIntrinsic; + } + + public CardTraitChanges copy(Card host, boolean lki) { + try { + CardTraitChanges result = (CardTraitChanges) super.clone(); + + result.abilities = Lists.newArrayList(); + for (SpellAbility sa : this.abilities) { + result.abilities.add(sa.copy(host, lki)); + } + result.removedAbilities = Lists.newArrayList(); + for (SpellAbility sa : this.removedAbilities) { + result.removedAbilities.add(sa.copy(host, lki)); + } + + result.triggers = Lists.newArrayList(); + for (Trigger tr : this.triggers) { + result.triggers.add(tr.copy(host, lki)); + } + + result.replacements = Lists.newArrayList(); + for (ReplacementEffect re : this.replacements) { + result.replacements.add(re.copy(host, lki)); + } + + result.staticAbilities = Lists.newArrayList(); + for (StaticAbility sa : this.staticAbilities) { + result.staticAbilities.add(sa.copy(host, lki)); + } + + return result; + } catch (final Exception ex) { + throw new RuntimeException("CardTraitChanges : clone() error", ex); + } + } +} 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 7d1a508062d..48849e677d2 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -29,9 +29,7 @@ import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.player.Player; -import forge.game.replacement.ReplacementEffect; import forge.game.spellability.*; -import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.util.TextUtil; import forge.util.collect.FCollection; @@ -240,23 +238,6 @@ public final class CardUtil { newCopy.setType(new CardType(in.getType())); newCopy.setToken(in.isToken()); - // extra copy non Intrinsic traits - for (SpellAbility sa : in.getSpellAbilities()) { - if (!sa.isIntrinsic()) { - newCopy.addSpellAbility(sa.copy(newCopy, true)); - } - } - for (Trigger tr : in.getTriggers()) { - if (!tr.isIntrinsic()) { - newCopy.moveTrigger(tr.copy(newCopy, true)); - } - } - for (ReplacementEffect re : in.getReplacementEffects()) { - if (!re.isIntrinsic()) { - newCopy.addReplacementEffect(re.copy(newCopy, true)); - } - } - // lock in the current P/T without bonus from counters newCopy.setBasePower(in.getCurrentPower() + in.getTempPowerBoost()); newCopy.setBaseToughness(in.getCurrentToughness() + in.getTempToughnessBoost()); @@ -293,6 +274,7 @@ public final class CardUtil { newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); newCopy.setChangedCardTypes(in.getChangedCardTypesMap()); newCopy.setChangedCardNames(in.getChangedCardNames()); + newCopy.setChangedCardTraits(in.getChangedCardTraits()); newCopy.copyChangedTextFrom(in); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 14ff90e380f..a187d2a101e 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1178,7 +1178,7 @@ public class Player extends GameEntity implements Comparable { com.remove(eff); eff.setStaticAbilities(Lists.newArrayList()); } - this.updateZoneForView(com); + this.updateZoneForView(com); } @Override diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java index 1007b1fc727..0c23c297da5 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java @@ -193,7 +193,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { res.setActiveZone(validHostZones); res.setLayer(getLayer()); - res.setTemporary(isTemporary()); return res; } 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 272388ef303..04f1e61cbda 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -356,31 +356,4 @@ public class ReplacementHandler { return ret; } - - public void cleanUpTemporaryReplacements() { - game.forEachCardInGame(new Visitor() { - @Override - public boolean visit(Card c) { - List toRemove = Lists.newArrayList(); - for (ReplacementEffect rep : c.getReplacementEffects()) { - if (rep.isTemporary()) { - toRemove.add(rep); - } - } - for (ReplacementEffect rep : toRemove) { - c.removeReplacementEffect(rep); - } - return true; - } - }); - game.forEachCardInGame(new Visitor() { - @Override - public boolean visit(Card c) { - for (int i = 0; i < c.getReplacementEffects().size(); i++) { - c.getReplacementEffects().get(i).setTemporarilySuppressed(false); - } - return true; - } - }); - } } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityPredicates.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityPredicates.java index 57e6b9ee717..4f575371492 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityPredicates.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityPredicates.java @@ -32,6 +32,15 @@ public final class SpellAbilityPredicates extends CardTraitPredicates { } }; } + + public static final Predicate isManaAbility() { + return new Predicate() { + @Override + public boolean apply(final SpellAbility sa) { + return sa.isManaAbility(); + } + }; + } public static final Predicate isIntrinsic() { return new Predicate() { diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index 45eebde5916..bad27de11c1 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -159,14 +159,14 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone } if (hasParam("RemoveAllAbilities") || hasParam("GainsAbilitiesOf")) { - layers.add(StaticAbilityLayer.ABILITIES1); + layers.add(StaticAbilityLayer.ABILITIES); } if (hasParam("AddKeyword") || hasParam("AddAbility") || hasParam("AddTrigger") || hasParam("RemoveTriggers") || hasParam("RemoveKeyword") || hasParam("AddReplacementEffects") || hasParam("AddStaticAbility") || hasParam("AddSVar")) { - layers.add(StaticAbilityLayer.ABILITIES2); + layers.add(StaticAbilityLayer.ABILITIES); } if (hasParam("CharacteristicDefining")) { @@ -183,7 +183,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone if (hasParam("AddHiddenKeyword")) { // special rule for can't have or gain if (getParam("AddHiddenKeyword").contains("can't have or gain")) { - layers.add(StaticAbilityLayer.ABILITIES1); + layers.add(StaticAbilityLayer.ABILITIES); } layers.add(StaticAbilityLayer.RULES); } @@ -259,14 +259,14 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone } public final CardCollectionView applyContinuousAbilityBefore(final StaticAbilityLayer layer, final CardCollectionView preList) { - if (!shouldApplyContinuousAbility(layer, false)) { + if (!shouldApplyContinuousAbility(layer)) { return null; } return StaticAbilityContinuous.applyContinuousAbility(this, layer, preList); } public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer, final CardCollectionView affected) { - if (!shouldApplyContinuousAbility(layer, true)) { + if (!shouldApplyContinuousAbility(layer)) { return null; } return StaticAbilityContinuous.applyContinuousAbility(this, affected, layer); @@ -286,14 +286,8 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone * affects the specified layer, it's not suppressed, and its * conditions are fulfilled. */ - private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean ignoreTempSuppression) { - final boolean isSuppressed; - if (ignoreTempSuppression) { - isSuppressed = this.isNonTempSuppressed(); - } else { - isSuppressed = this.isSuppressed(); - } - return getParam("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed && this.checkConditions(); + private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer) { + return getParam("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed() && this.checkConditions(); } // apply the ability if it has the right mode diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 5e584a7e324..87058d00c38 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -122,7 +122,6 @@ public final class StaticAbilityContinuous { String addColors = null; String[] addTriggers = null; String[] addStatics = null; - List addFullAbs = null; boolean removeAllAbilities = false; boolean removeIntrinsicAbilities = false; boolean removeNonMana = false; @@ -187,7 +186,7 @@ public final class StaticAbilityContinuous { toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb, true); } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddKeyword")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddKeyword")) { addKeywords = params.get("AddKeyword").split(" & "); final Iterable chosencolors = hostCard.getChosenColors(); for (final String color : chosencolors) { @@ -222,31 +221,31 @@ public final class StaticAbilityContinuous { } } - if ((layer == StaticAbilityLayer.RULES || layer == StaticAbilityLayer.ABILITIES1) && params.containsKey("AddHiddenKeyword")) { + if ((layer == StaticAbilityLayer.RULES || layer == StaticAbilityLayer.ABILITIES) && params.containsKey("AddHiddenKeyword")) { // can't have or gain, need to be applyed in ABILITIES1 for (String k : params.get("AddHiddenKeyword").split(" & ")) { - if ( (k.contains("can't have or gain")) == (layer == StaticAbilityLayer.ABILITIES1)) + if ( (k.contains("can't have or gain")) == (layer == StaticAbilityLayer.ABILITIES)) addHiddenKeywords.add(k); } } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("RemoveKeyword")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveKeyword")) { removeKeywords = params.get("RemoveKeyword").split(" & "); } - if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("RemoveAllAbilities")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveAllAbilities")) { removeAllAbilities = true; if (params.containsKey("ExceptManaAbilities")) { removeNonMana = true; } } // do this in type layer too in case of blood moon - if ((layer == StaticAbilityLayer.ABILITIES1 || layer == StaticAbilityLayer.TYPE) + if ((layer == StaticAbilityLayer.TYPE) && params.containsKey("RemoveIntrinsicAbilities")) { removeIntrinsicAbilities = true; } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddAbility")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddAbility")) { final String[] sVars = params.get("AddAbility").split(" & "); for (int i = 0; i < sVars.length; i++) { sVars[i] = hostCard.getSVar(sVars[i]); @@ -254,7 +253,7 @@ public final class StaticAbilityContinuous { addAbilities = sVars; } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddReplacementEffects")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddReplacementEffects")) { final String[] sVars = params.get("AddReplacementEffects").split(" & "); for (int i = 0; i < sVars.length; i++) { sVars[i] = hostCard.getSVar(sVars[i]); @@ -262,7 +261,7 @@ public final class StaticAbilityContinuous { addReplacements = sVars; } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddSVar")) { + if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddSVar")) { addSVars = params.get("AddSVar").split(" & "); } @@ -339,7 +338,7 @@ public final class StaticAbilityContinuous { } } - if (layer == StaticAbilityLayer.ABILITIES2) { + if (layer == StaticAbilityLayer.ABILITIES) { if (params.containsKey("AddTrigger")) { final String[] sVars = params.get("AddTrigger").split(" & "); for (int i = 0; i < sVars.length; i++) { @@ -357,45 +356,6 @@ public final class StaticAbilityContinuous { } } - if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("GainsAbilitiesOf")) { - final String[] valids = params.get("GainsAbilitiesOf").split(","); - List validZones; - final boolean loyaltyAB = params.containsKey("GainsLoyaltyAbilities"); - if (params.containsKey("GainsAbilitiesOfZones")) { - validZones = ZoneType.listValueOf(params.get("GainsAbilitiesOfZones")); - } else { - validZones = ImmutableList.of(ZoneType.Battlefield); - } - - CardCollectionView cardsIGainedAbilitiesFrom = game.getCardsIn(validZones); - cardsIGainedAbilitiesFrom = CardLists.getValidCards(cardsIGainedAbilitiesFrom, valids, hostCard.getController(), hostCard, null); - - if (cardsIGainedAbilitiesFrom.size() > 0) { - addFullAbs = Lists.newArrayList(); - - for (Card c : cardsIGainedAbilitiesFrom) { - for (SpellAbility sa : c.getSpellAbilities()) { - if (sa instanceof AbilityActivated) { - if (loyaltyAB && !sa.isPwAbility()) { - continue; - } - SpellAbility newSA = sa.copy(hostCard, false); - if (params.containsKey("GainsAbilitiesLimitPerTurn")) { - newSA.setRestrictions(sa.getRestrictions()); - newSA.getRestrictions().setLimitToCheck(params.get("GainsAbilitiesLimitPerTurn")); - } - newSA.setOriginalHost(c); - newSA.setOriginalAbility(sa); // need to be set to get the Once Per turn Clause correct - newSA.setGrantorStatic(stAb); - newSA.setIntrinsic(false); - newSA.setTemporary(true); - addFullAbs.add(newSA); - } - } - } - } - } - if (layer == StaticAbilityLayer.RULES) { // These fall under Rule changes, as they don't fit any other category if (params.containsKey("MayLookAt")) { @@ -444,7 +404,6 @@ public final class StaticAbilityContinuous { if (addStatics != null) { for (String s : addStatics) { StaticAbility stat = p.addStaticAbility(hostCard, s); - stat.setTemporary(true); stat.setIntrinsic(false); } } @@ -582,38 +541,117 @@ public final class StaticAbilityContinuous { } } - if (addFullAbs != null) { - for (final SpellAbility ab : addFullAbs) { - affectedCard.addSpellAbility(ab, false); + if (layer == StaticAbilityLayer.ABILITIES) { + + List addedAbilities = Lists.newArrayList(); + List addedReplacementEffects = Lists.newArrayList(); + List addedTrigger = Lists.newArrayList(); + List addedStaticAbility = Lists.newArrayList(); + // add abilities + if (addAbilities != null) { + for (String abilty : addAbilities) { + if (abilty.contains("CardManaCost")) { + abilty = TextUtil.fastReplace(abilty, "CardManaCost", affectedCard.getManaCost().getShortString()); + } else if (abilty.contains("ConvertedManaCost")) { + final String costcmc = Integer.toString(affectedCard.getCMC()); + abilty = TextUtil.fastReplace(abilty, "ConvertedManaCost", costcmc); + } + if (abilty.startsWith("AB") || abilty.startsWith("ST")) { // grant the ability + final SpellAbility sa = AbilityFactory.getAbility(abilty, affectedCard); + sa.setIntrinsic(false); + sa.setOriginalHost(hostCard); + addedAbilities.add(sa); + } + } + } + + if (params.containsKey("GainsAbilitiesOf")) { + final String[] valids = params.get("GainsAbilitiesOf").split(","); + List validZones; + final boolean loyaltyAB = params.containsKey("GainsLoyaltyAbilities"); + if (params.containsKey("GainsAbilitiesOfZones")) { + validZones = ZoneType.listValueOf(params.get("GainsAbilitiesOfZones")); + } else { + validZones = ImmutableList.of(ZoneType.Battlefield); + } + + CardCollectionView cardsIGainedAbilitiesFrom = game.getCardsIn(validZones); + cardsIGainedAbilitiesFrom = CardLists.getValidCards(cardsIGainedAbilitiesFrom, valids, hostCard.getController(), hostCard, null); + + for (Card c : cardsIGainedAbilitiesFrom) { + for (SpellAbility sa : c.getSpellAbilities()) { + if (sa instanceof AbilityActivated) { + if (loyaltyAB && !sa.isPwAbility()) { + continue; + } + SpellAbility newSA = sa.copy(affectedCard, false); + if (params.containsKey("GainsAbilitiesLimitPerTurn")) { + newSA.setRestrictions(sa.getRestrictions()); + newSA.getRestrictions().setLimitToCheck(params.get("GainsAbilitiesLimitPerTurn")); + } + newSA.setOriginalHost(c); + newSA.setOriginalAbility(sa); // need to be set to get the Once Per turn Clause correct + newSA.setGrantorStatic(stAb); + newSA.setIntrinsic(false); + addedAbilities.add(newSA); + } + } + } + } + + // add Replacement effects + if (addReplacements != null) { + for (String rep : addReplacements) { + final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false); + actualRep.setIntrinsic(false); + addedReplacementEffects.add(actualRep); + } + } + + // add triggers + if (addTriggers != null) { + for (final String trigger : addTriggers) { + final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false); + // if the trigger has Execute param, which most trigger gained by Static Abilties should have + // turn them into SpellAbility object before adding to card + // with that the TargetedCard does not need the Svars added to them anymore + // but only do it if the trigger doesn't already have a overriding ability + if (actualTrigger.hasParam("Execute") && actualTrigger.getOverridingAbility() == null) { + SpellAbility sa = AbilityFactory.getAbility(hostCard, actualTrigger.getParam("Execute")); + // set hostcard there so when the card is added to trigger, it doesn't make a copy of it + sa.setHostCard(affectedCard); + // set OriginalHost to get the owner of this static ability + sa.setOriginalHost(hostCard); + // set overriding ability to the trigger + actualTrigger.setOverridingAbility(sa); + } + actualTrigger.setIntrinsic(false); + addedTrigger.add(actualTrigger); + } + } + + // add static abilities + if (addStatics != null) { + for (String s : addStatics) { + if (s.contains("ConvertedManaCost")) { + final String costcmc = Integer.toString(affectedCard.getCMC()); + s = TextUtil.fastReplace(s, "ConvertedManaCost", costcmc); + } + + StaticAbility stat = new StaticAbility(s, affectedCard); + stat.setIntrinsic(false); + addedStaticAbility.add(stat); + } + } + + if (!addedAbilities.isEmpty() || addReplacements != null || addTriggers != null || addStatics != null + || removeAllAbilities) { + affectedCard.addChangedCardTraits(addedAbilities, null, addedTrigger, addedReplacementEffects, addedStaticAbility, removeAllAbilities, removeNonMana, false, hostCard.getTimestamp()); } } - // add abilities - if (addAbilities != null) { - for (String abilty : addAbilities) { - if (abilty.contains("CardManaCost")) { - abilty = TextUtil.fastReplace(abilty, "CardManaCost", affectedCard.getManaCost().getShortString()); - } else if (abilty.contains("ConvertedManaCost")) { - final String costcmc = Integer.toString(affectedCard.getCMC()); - abilty = TextUtil.fastReplace(abilty, "ConvertedManaCost", costcmc); - } - if (abilty.startsWith("AB") || abilty.startsWith("ST")) { // grant the ability - final SpellAbility sa = AbilityFactory.getAbility(abilty, affectedCard); - sa.setTemporary(true); - sa.setIntrinsic(false); - sa.setOriginalHost(hostCard); - affectedCard.addSpellAbility(sa, false); - } - } - } - - // add Replacement effects - if (addReplacements != null) { - for (String rep : addReplacements) { - final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false); - actualRep.setIntrinsic(false); - affectedCard.addReplacementEffect(actualRep).setTemporary(true); - } + if (layer == StaticAbilityLayer.TYPE && removeIntrinsicAbilities) { + affectedCard.addChangedCardTraits(null, null, null, null, null, false, false, removeIntrinsicAbilities, hostCard.getTimestamp()); } // add Types @@ -628,81 +666,6 @@ public final class StaticAbilityContinuous { affectedCard.addColor(addColors, !overwriteColors, hostCard.getTimestamp()); } - // add triggers - if (addTriggers != null) { - for (final String trigger : addTriggers) { - final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false); - // if the trigger has Execute param, which most trigger gained by Static Abilties should have - // turn them into SpellAbility object before adding to card - // with that the TargetedCard does not need the Svars added to them anymore - // but only do it if the trigger doesn't already have a overriding ability - if (actualTrigger.hasParam("Execute") && actualTrigger.getOverridingAbility() == null) { - SpellAbility sa = AbilityFactory.getAbility(hostCard, actualTrigger.getParam("Execute")); - // set hostcard there so when the card is added to trigger, it doesn't make a copy of it - sa.setHostCard(affectedCard); - // set OriginalHost to get the owner of this static ability - sa.setOriginalHost(hostCard); - // set overriding ability to the trigger - actualTrigger.setOverridingAbility(sa); - } - actualTrigger.setIntrinsic(false); - affectedCard.addTrigger(actualTrigger).setTemporary(true); - } - } - - // add static abilities - if (addStatics != null) { - for (String s : addStatics) { - if (s.contains("ConvertedManaCost")) { - final String costcmc = Integer.toString(affectedCard.getCMC()); - s = TextUtil.fastReplace(s, "ConvertedManaCost", costcmc); - } - - StaticAbility stat = affectedCard.addStaticAbility(s); - stat.setTemporary(true); - stat.setIntrinsic(false); - } - } - - // remove triggers - if ((layer == StaticAbilityLayer.ABILITIES2 && (params.containsKey("RemoveTriggers")) - || removeAllAbilities || removeIntrinsicAbilities)) { - for (final Trigger trigger : affectedCard.getTriggers()) { - if (removeAllAbilities || (removeIntrinsicAbilities && trigger.isIntrinsic())) { - trigger.setTemporarilySuppressed(true); - } - } - } - - // remove activated and static abilities - if (removeAllAbilities || removeIntrinsicAbilities) { - if (removeNonMana) { // Blood Sun - for (final SpellAbility mana : affectedCard.getNonManaAbilities()) { - if (removeAllAbilities - || (removeIntrinsicAbilities && mana.isIntrinsic() && !mana.isBasicLandAbility())) { - mana.setTemporarilySuppressed(true); - } - } - } else { - for (final SpellAbility ab : affectedCard.getSpellAbilities()) { - if (removeAllAbilities - || (removeIntrinsicAbilities && ab.isIntrinsic() && !ab.isBasicLandAbility())) { - ab.setTemporarilySuppressed(true); - } - } - } - for (final StaticAbility stA : affectedCard.getStaticAbilities()) { - if (removeAllAbilities || (removeIntrinsicAbilities && stA.isIntrinsic())) { - stA.setTemporarilySuppressed(true); - } - } - for (final ReplacementEffect rE : affectedCard.getReplacementEffects()) { - if (removeAllAbilities || (removeIntrinsicAbilities && rE.isIntrinsic())) { - rE.setTemporarilySuppressed(true); - } - } - } - if (layer == StaticAbilityLayer.RULES) { if (params.containsKey("Goad")) { affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); @@ -765,11 +728,11 @@ public final class StaticAbilityContinuous { } }; - addIgnore.setTemporary(true); + addIgnore.setIntrinsic(false); addIgnore.setApi(ApiType.InternalIgnoreEffect); addIgnore.setDescription(cost + " Ignore the effect until end of turn."); - sourceCard.addSpellAbility(addIgnore); + sourceCard.addChangedCardTraits(ImmutableList.of(addIgnore), null, null, null, null, false, false, false, sourceCard.getTimestamp()); final GameCommand removeIgnore = new GameCommand() { private static final long serialVersionUID = -5415775215053216360L; diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java index 290b220c2b8..48e9a75e05e 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java @@ -18,11 +18,8 @@ public enum StaticAbilityLayer { /** Layer 5 for color-changing effects. */ COLOR, - /** Layer 6 for ability-removing and -copying effects. */ - ABILITIES1, - - /** Layer 6 for ability-granting effects. */ - ABILITIES2, + /** Layer 6 for ability effects. */ + ABILITIES, /** Layer 7a for characteristic-defining power/toughness effects. */ CHARACTERISTIC, @@ -37,5 +34,5 @@ public enum StaticAbilityLayer { RULES; public final static ImmutableList CONTINUOUS_LAYERS = - ImmutableList.of(COPY, CONTROL, TEXT, TYPE, COLOR, ABILITIES1, ABILITIES2, CHARACTERISTIC, SETPT, MODIFYPT, RULES); + ImmutableList.of(COPY, CONTROL, TEXT, TYPE, COLOR, ABILITIES, CHARACTERISTIC, SETPT, MODIFYPT, RULES); } diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java index 2aa850822d4..c655f6ae904 100644 --- a/forge-game/src/main/java/forge/game/trigger/Trigger.java +++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java @@ -561,7 +561,6 @@ public abstract class Trigger extends TriggerReplacementBase { copy.setTriggerPhases(Lists.newArrayList(validPhases)); } copy.setActiveZone(validHostZones); - copy.setTemporary(isTemporary()); return copy; } 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 bd971ea8aa1..720e7301910 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -67,42 +67,6 @@ public class TriggerHandler { } public final void cleanUpTemporaryTriggers() { - game.forEachCardInGame(new Visitor() { - @Override - public boolean visit(Card c) { - boolean changed = false; - List toRemove = Lists.newArrayList(); - for (Trigger t : c.getTriggers()) { - if (t.isTemporary()) { - toRemove.add(t); - } - } - for (Trigger t : toRemove) { - changed = true; - c.removeTrigger(t); - } - if (changed) { - c.updateStateForView(); - } - return true; - } - }); - game.forEachCardInGame(new Visitor() { - @Override - public boolean visit(Card c) { - boolean changed = false; - for (int i = 0; i < c.getTriggers().size(); i++) { - if (c.getTriggers().get(i).isSuppressed()) { - c.getTriggers().get(i).setTemporarilySuppressed(false); - changed = true; - } - } - if (changed) { - c.updateStateForView(); - } - return true; - } - }); } public final boolean hasDelayedTriggers() { @@ -699,9 +663,6 @@ public class TriggerHandler { Player p = regtrig.getHostCard().getController(); p.getZone(ZoneType.Command).remove(regtrig.getHostCard()); } - else { - regtrig.getHostCard().removeTrigger(regtrig); - } } }