Merge branch 'cardTraitsReworkAnimate' into 'master'

Card: CardTraits are now not added to Card/State anymore but uses timestamp

Closes #1165, #1120, #955, and #573

See merge request core-developers/forge!2216
This commit is contained in:
Michael Kamensky
2019-10-05 19:59:28 +00:00
25 changed files with 487 additions and 698 deletions

View File

@@ -1266,7 +1266,7 @@ public class ComputerUtil {
public static boolean preventRunAwayActivations(final SpellAbility sa) { public static boolean preventRunAwayActivations(final SpellAbility sa) {
int activations = sa.getActivationsThisTurn(); int activations = sa.getActivationsThisTurn();
if (sa.isTemporary()) { if (!sa.isIntrinsic()) {
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
} }

View File

@@ -1128,14 +1128,14 @@ public class ComputerUtilCard {
// assume it either benefits the player or disrupts the opponent // assume it either benefits the player or disrupts the opponent
for (final StaticAbility stAb : c.getStaticAbilities()) { for (final StaticAbility stAb : c.getStaticAbilities()) {
final Map<String, String> params = stAb.getMapParams(); final Map<String, String> params = stAb.getMapParams();
if (params.get("Mode").equals("Continuous") && stAb.isIntrinsic() && !stAb.isTemporary()) { if (params.get("Mode").equals("Continuous") && stAb.isIntrinsic()) {
priority = true; priority = true;
break; break;
} }
} }
if (!priority) { if (!priority) {
for (final Trigger t : c.getTriggers()) { 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 // has a triggered ability, could be benefitting the opponent or disrupting the AI
priority = true; priority = true;
break; break;

View File

@@ -467,26 +467,19 @@ public class AnimateAi extends SpellAbilityAi {
AnimateEffectBase.doAnimate(card, sa, power, toughness, types, removeTypes, finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); 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 // remove abilities
final List<SpellAbility> removedAbilities = Lists.newArrayList(); final List<SpellAbility> removedAbilities = Lists.newArrayList();
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean clearSpells = sa.hasParam("OverwriteSpells");
boolean removeAll = sa.hasParam("RemoveAllAbilities"); boolean removeAll = sa.hasParam("RemoveAllAbilities");
boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities"); boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities");
if (clearAbilities || clearSpells || removeAll) { if (clearSpells) {
for (final SpellAbility ab : card.getSpellAbilities()) { removedAbilities.addAll(Lists.newArrayList(card.getSpells()));
if (removeAll }
|| (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())
|| (ab.isAbility() && clearAbilities) if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) {
|| (ab.isSpell() && clearSpells)) { removedAbilities.add(sa);
card.removeSpellAbility(ab);
removedAbilities.add(ab);
}
}
} }
// give abilities // give abilities
@@ -494,9 +487,7 @@ public class AnimateAi extends SpellAbilityAi {
if (abilities.size() > 0) { if (abilities.size() > 0) {
for (final String s : abilities) { for (final String s : abilities) {
final String actualAbility = source.getSVar(s); final String actualAbility = source.getSVar(s);
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, source); addedAbilities.add(AbilityFactory.getAbility(actualAbility, card));
addedAbilities.add(grantedAbility);
card.addSpellAbility(grantedAbility);
} }
} }
@@ -505,8 +496,8 @@ public class AnimateAi extends SpellAbilityAi {
if (triggers.size() > 0) { if (triggers.size() > 0) {
for (final String s : triggers) { for (final String s : triggers) {
final String actualTrigger = source.getSVar(s); final String actualTrigger = source.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, source, false); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, card, false);
addedTriggers.add(card.addTrigger(parsedTrigger)); addedTriggers.add(parsedTrigger);
} }
} }
@@ -515,24 +506,28 @@ public class AnimateAi extends SpellAbilityAi {
if (replacements.size() > 0) { if (replacements.size() > 0) {
for (final String s : replacements) { for (final String s : replacements) {
final String actualReplacement = source.getSVar(s); final String actualReplacement = source.getSVar(s);
final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, card, false);
source, false); addedReplacements.add(parsedReplacement);
addedReplacements.add(card.addReplacementEffect(parsedReplacement));
} }
} }
// suppress triggers from the animated card // give static abilities (should only be used by cards to give
final List<Trigger> removedTriggers = Lists.newArrayList(); // itself a static ability)
if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) { final List<StaticAbility> addedStaticAbilities = Lists.newArrayList();
for (final Trigger trigger : card.getTriggers()) { if (stAbs.size() > 0) {
if (removeIntrinsic && !trigger.isIntrinsic()) { for (final String s : stAbs) {
continue; final String actualAbility = source.getSVar(s);
} addedStaticAbilities.add(new StaticAbility(actualAbility, card));
trigger.setSuppressed(true);
removedTriggers.add(trigger);
} }
} }
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 // give static abilities (should only be used by cards to give
// itself a static ability) // itself a static ability)
if (stAbs.size() > 0) { if (stAbs.size() > 0) {
@@ -560,30 +555,6 @@ public class AnimateAi extends SpellAbilityAi {
card.setSVar(name, actualsVar); card.setSVar(name, actualsVar);
} }
} }
// suppress static abilities from the animated card
final List<StaticAbility> 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<ReplacementEffect> 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); ComputerUtilCard.applyStaticContPT(game, card, null);
} }

View File

@@ -38,15 +38,9 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
/** The is intrinsic. */ /** The is intrinsic. */
protected boolean intrinsic; protected boolean intrinsic;
/** The temporary. */
protected boolean temporary = false;
/** The suppressed. */ /** The suppressed. */
protected boolean suppressed = false; protected boolean suppressed = false;
/** The temporarily suppressed. */
protected boolean temporarilySuppressed = false;
protected Map<String, String> sVars = Maps.newHashMap(); protected Map<String, String> sVars = Maps.newHashMap();
protected Map<String, String> intrinsicChangedTextColors = Maps.newHashMap(); protected Map<String, String> intrinsicChangedTextColors = Maps.newHashMap();
@@ -66,24 +60,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
*/ */
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder() private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
.add("TokenScript", "LegacyImage", "TokenImage", "NewName").build(); .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;
}
/** /**
* <p> * <p>
@@ -196,26 +172,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
this.suppressed = supp; 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. * Checks if is suppressed.
* *
* @return true, if is suppressed * @return true, if is suppressed
*/ */
public final boolean isSuppressed() { public final boolean isSuppressed() {
return (this.suppressed || this.temporarilySuppressed);
}
protected final boolean isNonTempSuppressed() {
return this.suppressed; return this.suppressed;
} }

View File

@@ -760,8 +760,6 @@ public class GameAction {
// remove old effects // remove old effects
game.getStaticEffects().clearStaticEffects(affectedCards); game.getStaticEffects().clearStaticEffects(affectedCards);
game.getTriggerHandler().cleanUpTemporaryTriggers();
game.getReplacementHandler().cleanUpTemporaryReplacements();
for (final Player p : game.getPlayers()) { for (final Player p : game.getPlayers()) {
if (!game.getStack().isFrozen()) { if (!game.getStack().isFrozen()) {
@@ -779,17 +777,11 @@ public class GameAction {
public boolean visit(final Card c) { public boolean visit(final Card c) {
// need to get Card from preList if able // need to get Card from preList if able
final Card co = preList.get(c); final Card co = preList.get(c);
List<StaticAbility> toRemove = Lists.newArrayList();
for (StaticAbility stAb : co.getStaticAbilities()) { for (StaticAbility stAb : co.getStaticAbilities()) {
if (stAb.isTemporary()) { if (stAb.getParam("Mode").equals("Continuous")) {
toRemove.add(stAb);
} else if (stAb.getParam("Mode").equals("Continuous")) {
staticAbilities.add(stAb); staticAbilities.add(stAb);
} }
} }
for (StaticAbility stAb : toRemove) {
co.removeStaticAbility(stAb);
}
if (!co.getStaticCommandList().isEmpty()) { if (!co.getStaticCommandList().isEmpty()) {
staticList.add(co); staticList.add(co);
} }

View File

@@ -23,8 +23,6 @@ import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardUtil; import forge.game.card.CardUtil;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import java.util.ArrayList; import java.util.ArrayList;
@@ -236,11 +234,7 @@ public class StaticEffect {
} }
if (hasParam("IgnoreEffectCost")) { if (hasParam("IgnoreEffectCost")) {
for (final SpellAbility s : getSource().getSpellAbilities()) { getSource().removeChangedCardTraits(getTimestamp());
if (s instanceof AbilityStatic && s.isTemporary()) {
getSource().removeSpellAbility(s);
}
}
} }
// modify players // modify players
@@ -273,22 +267,12 @@ public class StaticEffect {
// the view is updated in GameAction#checkStaticAbilities to avoid flickering // the view is updated in GameAction#checkStaticAbilities to avoid flickering
// remove keywords // remove keywords
// TODO regular keywords currently don't try to use keyword multiplier
// (Although nothing uses it at this time) // (Although nothing uses it at this time)
if (hasParam("AddKeyword") || hasParam("RemoveKeyword") if (hasParam("AddKeyword") || hasParam("RemoveKeyword")
|| hasParam("RemoveAllAbilities")) { || hasParam("RemoveAllAbilities")) {
affectedCard.removeChangedCardKeywords(getTimestamp()); 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) { if (addHiddenKeywords != null) {
for (final String k : addHiddenKeywords) { for (final String k : addHiddenKeywords) {
affectedCard.removeHiddenExtrinsicKeyword(k); affectedCard.removeHiddenExtrinsicKeyword(k);
@@ -296,8 +280,10 @@ public class StaticEffect {
} }
// remove abilities // remove abilities
if (hasParam("RemoveAllAbilities") || hasParam("RemoveIntrinsicAbilities")) { if (hasParam("AddAbility") || hasParam("GainsAbilitiesOf")
affectedCard.unSuppressCardTraits(); || hasParam("AddTrigger") || hasParam("AddStaticAbility") || hasParam("AddReplacementEffects")
|| hasParam("RemoveAllAbilities") || hasParam("RemoveIntrinsicAbilities")) {
affectedCard.removeChangedCardTraits(getTimestamp());
} }
// remove Types // remove Types

View File

@@ -80,4 +80,13 @@ public class StaticEffects {
public Iterable<StaticEffect> getEffects() { public Iterable<StaticEffect> getEffects() {
return staticEffects.values(); return staticEffects.values();
} }
public boolean removeStaticEffect(final StaticAbility staticAbility) {
final StaticEffect currentEffect = staticEffects.remove(staticAbility);
if (currentEffect == null) {
return false;
}
currentEffect.remove();
return true;
}
} }

View File

@@ -14,19 +14,15 @@ import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementHandler;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerHandler;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.collect.FCollectionView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.ImmutableList;
public class AnimateAllEffect extends AnimateEffectBase { public class AnimateAllEffect extends AnimateEffectBase {
@Override @Override
@@ -160,30 +156,16 @@ public class AnimateAllEffect extends AnimateEffectBase {
final String actualAbility = host.getSVar(s); final String actualAbility = host.getSVar(s);
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
addedAbilities.add(grantedAbility); addedAbilities.add(grantedAbility);
c.addSpellAbility(grantedAbility);
} }
} }
// remove abilities
final List<SpellAbility> 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 // give replacement effects
final List<ReplacementEffect> addedReplacements = new ArrayList<>(); final List<ReplacementEffect> addedReplacements = new ArrayList<>();
if (replacements.size() > 0) { if (replacements.size() > 0) {
for (final String s : replacements) { for (final String s : replacements) {
final String actualReplacement = host.getSVar(s); final String actualReplacement = host.getSVar(s);
final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false); final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false);
addedReplacements.add(c.addReplacementEffect(parsedReplacement)); addedReplacements.add(parsedReplacement);
} }
} }
// Grant triggers // Grant triggers
@@ -192,45 +174,12 @@ public class AnimateAllEffect extends AnimateEffectBase {
for (final String s : triggers) { for (final String s : triggers) {
final String actualTrigger = host.getSVar(s); final String actualTrigger = host.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false);
addedTriggers.add(c.addTrigger(parsedTrigger)); addedTriggers.add(parsedTrigger);
} }
} }
// suppress triggers from the animated card if (!addedAbilities.isEmpty() || !addedTriggers.isEmpty() || !addedReplacements.isEmpty()) {
final List<Trigger> removedTriggers = new ArrayList<>(); c.addChangedCardTraits(addedAbilities, null, addedTriggers, addedReplacements, null, removeAll, false, removeIntrinsic, timestamp);
if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) {
final FCollectionView<Trigger> 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<StaticAbility> 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<ReplacementEffect> 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);
}
} }
// give sVars // give sVars
@@ -247,27 +196,8 @@ public class AnimateAllEffect extends AnimateEffectBase {
@Override @Override
public void run() { public void run() {
doUnanimate(c, sa, finalDesc, hiddenKeywords, doUnanimate(c, sa, hiddenKeywords, timestamp);
addedAbilities, addedTriggers, addedReplacements,
ImmutableList.of(), 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)); game.fireEvent(new GameEventCardStatsChanged(c));
} }
}; };

View File

@@ -162,25 +162,15 @@ public class AnimateEffect extends AnimateEffectBase {
// remove abilities // remove abilities
final List<SpellAbility> removedAbilities = Lists.newArrayList(); final List<SpellAbility> removedAbilities = Lists.newArrayList();
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean clearSpells = sa.hasParam("OverwriteSpells");
boolean removeAll = sa.hasParam("RemoveAllAbilities"); boolean removeAll = sa.hasParam("RemoveAllAbilities");
boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities"); boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities");
if (clearAbilities || clearSpells || removeAll) { if (clearSpells) {
for (final SpellAbility ab : c.getSpellAbilities()) { removedAbilities.addAll(Lists.newArrayList(c.getSpells()));
if (removeAll
|| (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())
|| (ab.isAbility() && clearAbilities)
|| (ab.isSpell() && clearSpells)) {
ab.setTemporarilySuppressed(true);
removedAbilities.add(ab);
}
}
} }
if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) { if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) {
c.removeSpellAbility(sa);
removedAbilities.add(sa); removedAbilities.add(sa);
} }
@@ -189,9 +179,7 @@ public class AnimateEffect extends AnimateEffectBase {
if (abilities.size() > 0) { if (abilities.size() > 0) {
for (final String s : abilities) { for (final String s : abilities) {
final String actualAbility = source.getSVar(s); final String actualAbility = source.getSVar(s);
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); addedAbilities.add(AbilityFactory.getAbility(actualAbility, c));
addedAbilities.add(grantedAbility);
c.addSpellAbility(grantedAbility);
} }
} }
@@ -201,7 +189,7 @@ public class AnimateEffect extends AnimateEffectBase {
for (final String s : triggers) { for (final String s : triggers) {
final String actualTrigger = source.getSVar(s); final String actualTrigger = source.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false); 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) { for (final String s : replacements) {
final String actualReplacement = source.getSVar(s); final String actualReplacement = source.getSVar(s);
final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false); final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, c, false);
addedReplacements.add(c.addReplacementEffect(parsedReplacement)); addedReplacements.add(parsedReplacement);
}
}
// suppress triggers from the animated card
final List<Trigger> 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);
} }
} }
@@ -233,7 +209,7 @@ public class AnimateEffect extends AnimateEffectBase {
if (stAbs.size() > 0) { if (stAbs.size() > 0) {
for (final String s : stAbs) { for (final String s : stAbs) {
final String actualAbility = source.getSVar(s); 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<StaticAbility> 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<ReplacementEffect> 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 // give Remembered
if (animateRemembered != null) { if (animateRemembered != null) {
for (final Object o : AbilityUtils.getDefinedObjects(source, animateRemembered, sa)) { for (final Object o : AbilityUtils.getDefinedObjects(source, animateRemembered, sa)) {
@@ -287,35 +239,40 @@ public class AnimateEffect extends AnimateEffectBase {
@Override @Override
public void run() { public void run() {
doUnanimate(c, sa, finalDesc, hiddenKeywords, doUnanimate(c, sa, hiddenKeywords, timestamp);
addedAbilities, addedTriggers, addedReplacements,
addedStaticAbilities, timestamp);
c.removeChangedName(timestamp); c.removeChangedName(timestamp);
c.updateStateForView(); c.updateStateForView();
game.fireEvent(new GameEventCardStatsChanged(c)); 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 (!permanent) {
if (sa.hasParam("UntilEndOfCombat")) { if (sa.hasParam("UntilEndOfCombat")) {
game.getEndOfCombat().addUntil(unanimate); 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)); game.fireEvent(new GameEventCardStatsChanged(c));
} }

View File

@@ -20,10 +20,8 @@ package forge.game.ability.effects;
import forge.card.CardType; import forge.card.CardType;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import java.util.List; import java.util.List;
public abstract class AnimateEffectBase extends SpellAbilityEffect { public abstract class AnimateEffectBase extends SpellAbilityEffect {
@@ -128,14 +126,8 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
* @param timestamp * @param timestamp
* a long. * a long.
*/ */
static void doUnanimate(final Card c, SpellAbility sa, final String colorDesc, static void doUnanimate(final Card c, SpellAbility sa,
final List<String> hiddenKeywords, final List<SpellAbility> addedAbilities, final List<String> hiddenKeywords, final long timestamp) {
final List<Trigger> addedTriggers, final List<ReplacementEffect> addedReplacements,
final List<StaticAbility> addedStaticAbilities, final long timestamp) {
if (sa.hasParam("LastsIndefinitely")) {
return;
}
c.removeNewPT(timestamp); c.removeNewPT(timestamp);
@@ -144,26 +136,12 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.removeChangedCardTypes(timestamp); c.removeChangedCardTypes(timestamp);
c.removeColor(timestamp); c.removeColor(timestamp);
c.removeChangedCardTraits(timestamp);
for (final String k : hiddenKeywords) { for (final String k : hiddenKeywords) {
c.removeHiddenExtrinsicKeyword(k); 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 // any other unanimate cleanup
if (!c.isCreature()) { if (!c.isCreature()) {
c.unEquipAllCards(); c.unEquipAllCards();

View File

@@ -58,7 +58,7 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
+ " | TriggerDescription$ " + trigSA.getDescription(); + " | TriggerDescription$ " + trigSA.getDescription();
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, eff, true); final Trigger trigger = TriggerHandler.parseTrigger(trigStr, eff, true);
trigger.setOverridingAbility(trigSA); trigger.setOverridingAbility(trigSA);
eff.moveTrigger(trigger); eff.addTrigger(trigger);
} }
// Copy text changes // Copy text changes

View File

@@ -55,7 +55,6 @@ public class RestartGameEffect extends SpellAbilityEffect {
forge.game.trigger.Trigger.resetIDs(); forge.game.trigger.Trigger.resetIDs();
TriggerHandler trigHandler = game.getTriggerHandler(); TriggerHandler trigHandler = game.getTriggerHandler();
trigHandler.clearDelayedTrigger(); trigHandler.clearDelayedTrigger();
trigHandler.cleanUpTemporaryTriggers();
trigHandler.suppressMode(TriggerType.ChangesZone); trigHandler.suppressMode(TriggerType.ChangesZone);
game.getStack().reset(); game.getStack().reset();

View File

@@ -18,6 +18,7 @@
package forge.game.card; package forge.game.card;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicates;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.GameCommand; import forge.GameCommand;
@@ -121,6 +122,7 @@ public class Card extends GameEntity implements Comparable<Card> {
private final Map<Long, CardChangedType> changedCardTypes = Maps.newTreeMap(); private final Map<Long, CardChangedType> changedCardTypes = Maps.newTreeMap();
private final NavigableMap<Long, String> changedCardNames = Maps.newTreeMap(); private final NavigableMap<Long, String> changedCardNames = Maps.newTreeMap();
private final Map<Long, KeywordsChange> changedCardKeywords = Maps.newTreeMap(); private final Map<Long, KeywordsChange> changedCardKeywords = Maps.newTreeMap();
private final Map<Long, CardTraitChanges> changedCardTraits = Maps.newTreeMap();
private final Map<Long, CardColor> changedCardColors = Maps.newTreeMap(); private final Map<Long, CardColor> changedCardColors = Maps.newTreeMap();
private final NavigableMap<Long, CardCloneStates> clonedStates = Maps.newTreeMap(); private final NavigableMap<Long, CardCloneStates> clonedStates = Maps.newTreeMap();
private final NavigableMap<Long, CardCloneStates> textChangeStates = Maps.newTreeMap(); private final NavigableMap<Long, CardCloneStates> textChangeStates = Maps.newTreeMap();
@@ -1012,28 +1014,15 @@ public class Card extends GameEntity implements Comparable<Card> {
public final FCollectionView<Trigger> getTriggers() { public final FCollectionView<Trigger> getTriggers() {
return currentState.getTriggers(); return currentState.getTriggers();
} }
// only used for LKI
public final void setTriggers(final Iterable<Trigger> trigs, boolean intrinsicOnly) {
final FCollection<Trigger> 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) { 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); currentState.addTrigger(t);
return t;
} }
@Deprecated
public final void removeTrigger(final Trigger t) { public final void removeTrigger(final Trigger t) {
currentState.removeTrigger(t); currentState.removeTrigger(t);
} }
@Deprecated
public final void removeTrigger(final Trigger t, final CardStateName state) { public final void removeTrigger(final Trigger t, final CardStateName state) {
getState(state).removeTrigger(t); getState(state).removeTrigger(t);
} }
@@ -1050,6 +1039,25 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public void updateTriggers(List<Trigger> list, CardState state) { public void updateTriggers(List<Trigger> 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)) { for (KeywordInterface kw : getUnhiddenKeywords(state)) {
list.addAll(kw.getTriggers()); list.addAll(kw.getTriggers());
} }
@@ -2379,10 +2387,12 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
@Deprecated
public final void removeSpellAbility(final SpellAbility a) { public final void removeSpellAbility(final SpellAbility a) {
removeSpellAbility(a, true); removeSpellAbility(a, true);
} }
@Deprecated
public final void removeSpellAbility(final SpellAbility a, final boolean updateView) { public final void removeSpellAbility(final SpellAbility a, final boolean updateView) {
if (currentState.removeSpellAbility(a) && updateView) { if (currentState.removeSpellAbility(a) && updateView) {
currentState.getView().updateAbilityText(this, currentState); currentState.getView().updateAbilityText(this, currentState);
@@ -2408,15 +2418,44 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public void updateSpellAbilities(List<SpellAbility> list, CardState state, Boolean mana) { public void updateSpellAbilities(List<SpellAbility> 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 // do Basic Land Abilities there
if (mana == null || mana == true) { if (null == mana || true == mana) {
updateBasicLandAbilities(list, state); updateBasicLandAbilities(list, state);
} }
for (final CardTraitChanges ck : changedCardTraits.values()) {
if (ck.isRemoveNonMana()) {
// List only has nonMana
if (null == mana) {
List<SpellAbility> 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 // add Facedown abilities from Original state but only if this state is face down
// need CardStateView#getState or might crash in StackOverflow // need CardStateView#getState or might crash in StackOverflow
if (isInZone(ZoneType.Battlefield)) { 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()) { for (SpellAbility sa : getState(CardStateName.Original).getNonManaAbilities()) {
if (sa.isManifestUp() || sa.isMorphUp()) { if (sa.isManifestUp() || sa.isMorphUp()) {
list.add(sa); list.add(sa);
@@ -2729,14 +2768,14 @@ public class Card extends GameEntity implements Comparable<Card> {
* *
* Control, Controller: "Control" is the system that determines who gets to use an object in the game. * 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. * 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. * 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, * 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. * 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 * 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 * 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 * 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 objects CONTROLLER—or its OWNER * mutually exclusive things to a particular object, that objects CONTROLLER—or its OWNER
* IF IT HAS NO CONTROLLER—chooses which effect to apply, and what that effect does. * IF IT HAS NO CONTROLLER—chooses which effect to apply, and what that effect does.
*/ */
if (controller != null) { if (controller != null) {
@@ -3589,6 +3628,31 @@ public class Card extends GameEntity implements Comparable<Card> {
getGame().fireEvent(new GameEventCardTapped(this, false)); getGame().fireEvent(new GameEventCardTapped(this, false));
} }
public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> 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<Long, CardTraitChanges> getChangedCardTraits() {
return changedCardTraits;
}
public final void setChangedCardTraits(Map<Long, CardTraitChanges> changes) {
changedCardTraits.clear();
for (Entry<Long, CardTraitChanges> e : changes.entrySet()) {
changedCardTraits.put(e.getKey(), e.getValue().copy(this, true));
}
}
// keywords are like flying, fear, first strike, etc... // keywords are like flying, fear, first strike, etc...
public final List<KeywordInterface> getKeywords() { public final List<KeywordInterface> getKeywords() {
return getKeywords(currentState); return getKeywords(currentState);
@@ -4018,11 +4082,31 @@ public class Card extends GameEntity implements Comparable<Card> {
currentState.addStaticAbility(stAb); currentState.addStaticAbility(stAb);
return stAb; return stAb;
} }
@Deprecated
public final void removeStaticAbility(StaticAbility stAb) { public final void removeStaticAbility(StaticAbility stAb) {
currentState.removeStaticAbility(stAb); currentState.removeStaticAbility(stAb);
} }
public void updateStaticAbilities(List<StaticAbility> list, CardState state) { public void updateStaticAbilities(List<StaticAbility> 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)) { for (KeywordInterface kw : getUnhiddenKeywords(state)) {
list.addAll(kw.getStaticAbilities()); list.addAll(kw.getStaticAbilities());
} }
@@ -5641,24 +5725,35 @@ public class Card extends GameEntity implements Comparable<Card> {
return currentState.getReplacementEffects(); return currentState.getReplacementEffects();
} }
public void setReplacementEffects(final Iterable<ReplacementEffect> res) {
currentState.clearReplacementEffects();
for (final ReplacementEffect replacementEffect : res) {
if (replacementEffect.isIntrinsic()) {
addReplacementEffect(replacementEffect.copy(this, false));
}
}
}
public ReplacementEffect addReplacementEffect(final ReplacementEffect replacementEffect) { public ReplacementEffect addReplacementEffect(final ReplacementEffect replacementEffect) {
currentState.addReplacementEffect(replacementEffect); currentState.addReplacementEffect(replacementEffect);
return replacementEffect; return replacementEffect;
} }
@Deprecated
public void removeReplacementEffect(ReplacementEffect replacementEffect) { public void removeReplacementEffect(ReplacementEffect replacementEffect) {
currentState.removeReplacementEffect(replacementEffect); currentState.removeReplacementEffect(replacementEffect);
} }
public void updateReplacementEffects(List<ReplacementEffect> list, CardState state) { public void updateReplacementEffects(List<ReplacementEffect> 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)) { for (KeywordInterface kw : getUnhiddenKeywords(state)) {
list.addAll(kw.getReplacements()); list.addAll(kw.getReplacements());
} }
@@ -6296,27 +6391,6 @@ public class Card extends GameEntity implements Comparable<Card> {
withFlash.removeAll(timestamp); 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) { public boolean canBeDiscardedBy(SpellAbility sa) {
if (!isInZone(ZoneType.Hand)) { if (!isInZone(ZoneType.Hand)) {
return false; return false;

View File

@@ -3758,7 +3758,6 @@ public class CardFactoryUtil {
newSA.setDescription(sa.getDescription() + " (by paying " + cost.toSimpleString() + " instead of its mana cost)"); newSA.setDescription(sa.getDescription() + " (by paying " + cost.toSimpleString() + " instead of its mana cost)");
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} }
@@ -3788,8 +3787,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.equals("Aftermath") && card.getCurrentStateName().equals(CardStateName.RightSplit)) { } else if (keyword.equals("Aftermath") && card.getCurrentStateName().equals(CardStateName.RightSplit)) {
// Aftermath does modify existing SA, and does not add new one // 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); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Awaken")) { } else if (keyword.startsWith("Awaken")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -3835,8 +3830,6 @@ public class CardFactoryUtil {
awakenSpell.setBasicSpell(false); awakenSpell.setBasicSpell(false);
awakenSpell.setPayCosts(awakenCost); awakenSpell.setPayCosts(awakenCost);
awakenSpell.setIntrinsic(intrinsic); awakenSpell.setIntrinsic(intrinsic);
awakenSpell.setTemporary(!intrinsic);
inst.addSpellAbility(awakenSpell); inst.addSpellAbility(awakenSpell);
} else if (keyword.startsWith("Bestow")) { } else if (keyword.startsWith("Bestow")) {
final String[] params = keyword.split(":"); final String[] params = keyword.split(":");
@@ -3854,8 +3847,6 @@ public class CardFactoryUtil {
sa.setStackDescription("Bestow - " + card.getName()); sa.setStackDescription("Bestow - " + card.getName());
sa.setBasicSpell(false); sa.setBasicSpell(false);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Dash")) { } else if (keyword.startsWith("Dash")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -3865,8 +3856,6 @@ public class CardFactoryUtil {
final SpellAbility newSA = AbilityFactory.getAbility(dashString, card); final SpellAbility newSA = AbilityFactory.getAbility(dashString, card);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Emerge")) { } else if (keyword.startsWith("Emerge")) {
final String[] kw = keyword.split(":"); final String[] kw = keyword.split(":");
@@ -3882,8 +3871,6 @@ public class CardFactoryUtil {
newSA.setPayCosts(new Cost(costStr, false)); newSA.setPayCosts(new Cost(costStr, false));
newSA.setDescription(sa.getDescription() + " (Emerge)"); newSA.setDescription(sa.getDescription() + " (Emerge)");
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Embalm")) { } else if (keyword.startsWith("Embalm")) {
final String[] kw = keyword.split(":"); final String[] kw = keyword.split(":");
@@ -3896,8 +3883,6 @@ public class CardFactoryUtil {
"| SpellDescription$ (" + inst.getReminderText() + ")" ; "| SpellDescription$ (" + inst.getReminderText() + ")" ;
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.equals("Epic")) { } else if (keyword.equals("Epic")) {
// Epic does modify existing SA, and does not add new one // Epic does modify existing SA, and does not add new one
@@ -3952,7 +3937,6 @@ public class CardFactoryUtil {
// instantiate attach ability // instantiate attach ability
final SpellAbility newSA = AbilityFactory.getAbility(abilityStr.toString(), card); final SpellAbility newSA = AbilityFactory.getAbility(abilityStr.toString(), card);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Eternalize")) { } else if (keyword.startsWith("Eternalize")) {
final String[] kw = keyword.split(":"); final String[] kw = keyword.split(":");
@@ -3982,8 +3966,6 @@ public class CardFactoryUtil {
.append("| SpellDescription$ (").append(inst.getReminderText()).append(")"); .append("| SpellDescription$ (").append(inst.getReminderText()).append(")");
final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Evoke")) { } else if (keyword.startsWith("Evoke")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4005,8 +3987,6 @@ public class CardFactoryUtil {
newSA.setBasicSpell(false); newSA.setBasicSpell(false);
newSA.setEvoke(true); newSA.setEvoke(true);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Fortify")) { } else if (keyword.startsWith("Fortify")) {
String[] k = keyword.split(":"); String[] k = keyword.split(":");
@@ -4027,14 +4007,10 @@ public class CardFactoryUtil {
// instantiate attach ability // instantiate attach ability
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card); final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Fuse") && card.getCurrentStateName().equals(CardStateName.Original)) { } else if (keyword.startsWith("Fuse") && card.getCurrentStateName().equals(CardStateName.Original)) {
final SpellAbility sa = AbilityFactory.buildFusedAbility(card); final SpellAbility sa = AbilityFactory.buildFusedAbility(card);
card.addSpellAbility(sa); card.addSpellAbility(sa);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Haunt")) { } else if (keyword.startsWith("Haunt")) {
if (!card.isCreature() && intrinsic) { if (!card.isCreature() && intrinsic) {
@@ -4067,8 +4043,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Monstrosity")) { } else if (keyword.startsWith("Monstrosity")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4103,8 +4077,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Morph")) { } else if (keyword.startsWith("Morph")) {
@@ -4146,8 +4118,6 @@ public class CardFactoryUtil {
SpellAbility sa = AbilityFactory.getAbility(effect, card); SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
// extra secondary effect for Commander Ninjutsu // extra secondary effect for Commander Ninjutsu
@@ -4161,8 +4131,6 @@ public class CardFactoryUtil {
sa = AbilityFactory.getAbility(effect, card); sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} }
} else if (keyword.startsWith("Outlast")) { } else if (keyword.startsWith("Outlast")) {
@@ -4187,8 +4155,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card); final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Prowl")) { } else if (keyword.startsWith("Prowl")) {
@@ -4212,7 +4178,6 @@ public class CardFactoryUtil {
newSA.setProwl(true); newSA.setProwl(true);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Reinforce")) { } else if (keyword.startsWith("Reinforce")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4234,8 +4199,6 @@ public class CardFactoryUtil {
if (n.equals("X")) { if (n.equals("X")) {
sa.setSVar("X", "Count$xPaid"); sa.setSVar("X", "Count$xPaid");
} }
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Scavenge")) { } else if (keyword.startsWith("Scavenge")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4250,8 +4213,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("ScavengeX", "Count$CardPower"); sa.setSVar("ScavengeX", "Count$CardPower");
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Spectacle")) { } else if (keyword.startsWith("Spectacle")) {
@@ -4267,8 +4228,6 @@ public class CardFactoryUtil {
newSA.setDescription(desc); newSA.setDescription(desc);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Surge")) { } else if (keyword.startsWith("Surge")) {
@@ -4284,8 +4243,6 @@ public class CardFactoryUtil {
newSA.setDescription(desc); newSA.setDescription(desc);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Suspend") && !keyword.equals("Suspend")) { } else if (keyword.startsWith("Suspend") && !keyword.equals("Suspend")) {
@@ -4336,8 +4293,6 @@ public class CardFactoryUtil {
suspend.setStackDescription(sbStack.toString()); suspend.setStackDescription(sbStack.toString());
suspend.getRestrictions().setZone(ZoneType.Hand); suspend.getRestrictions().setZone(ZoneType.Hand);
suspend.setTemporary(!intrinsic);
inst.addSpellAbility(suspend); inst.addSpellAbility(suspend);
} else if (keyword.startsWith("Transfigure")) { } else if (keyword.startsWith("Transfigure")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4351,8 +4306,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("TransfigureX", "Count$CardManaCost"); sa.setSVar("TransfigureX", "Count$CardManaCost");
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Transmute")) { } else if (keyword.startsWith("Transmute")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4367,8 +4320,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("TransmuteX", "Count$CardManaCost"); sa.setSVar("TransmuteX", "Count$CardManaCost");
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Unearth")) { } else if (keyword.startsWith("Unearth")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -4383,8 +4334,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.endsWith(" offering")) { } else if (keyword.endsWith(" offering")) {
@@ -4403,8 +4352,6 @@ public class CardFactoryUtil {
newSA.setPayCosts(sa.getPayCosts()); newSA.setPayCosts(sa.getPayCosts());
newSA.setDescription(sa.getDescription() + " (" + offeringType + " offering)"); newSA.setDescription(sa.getDescription() + " (" + offeringType + " offering)");
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Crew")) { } else if (keyword.startsWith("Crew")) {
@@ -4420,8 +4367,6 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(effect, card); final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Cycling")) { } else if (keyword.startsWith("Cycling")) {
@@ -4440,8 +4385,6 @@ public class CardFactoryUtil {
SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
sa.setIsCycling(true); sa.setIsCycling(true);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("TypeCycling")) { } else if (keyword.startsWith("TypeCycling")) {
@@ -4466,8 +4409,6 @@ public class CardFactoryUtil {
SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card); SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
sa.setIsCycling(true); sa.setIsCycling(true);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} }

View File

@@ -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<Trigger> triggers = Lists.newArrayList();
private List<ReplacementEffect> replacements = Lists.newArrayList();
private List<SpellAbility> abilities = Lists.newArrayList();
private List<StaticAbility> staticAbilities = Lists.newArrayList();
private List<SpellAbility> removedAbilities = Lists.newArrayList();
private boolean removeAll;
private boolean removeNonMana;
private boolean removeIntrinsic;
public CardTraitChanges(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> res, Collection<StaticAbility> 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<Trigger> getTriggers() {
return triggers;
}
/**
* @return the replacements
*/
public Collection<ReplacementEffect> getReplacements() {
return replacements;
}
/**
* @return the abilities
*/
public Collection<SpellAbility> getAbilities() {
return abilities;
}
/**
* @return the abilities
*/
public Collection<SpellAbility> getRemovedAbilities() {
return removedAbilities;
}
/**
* @return the staticAbilities
*/
public Collection<StaticAbility> 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);
}
}
}

View File

@@ -29,9 +29,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*; import forge.game.spellability.*;
import forge.game.trigger.Trigger;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
@@ -240,23 +238,6 @@ public final class CardUtil {
newCopy.setType(new CardType(in.getType())); newCopy.setType(new CardType(in.getType()));
newCopy.setToken(in.isToken()); 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 // lock in the current P/T without bonus from counters
newCopy.setBasePower(in.getCurrentPower() + in.getTempPowerBoost()); newCopy.setBasePower(in.getCurrentPower() + in.getTempPowerBoost());
newCopy.setBaseToughness(in.getCurrentToughness() + in.getTempToughnessBoost()); newCopy.setBaseToughness(in.getCurrentToughness() + in.getTempToughnessBoost());
@@ -293,6 +274,7 @@ public final class CardUtil {
newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); newCopy.setChangedCardKeywords(in.getChangedCardKeywords());
newCopy.setChangedCardTypes(in.getChangedCardTypesMap()); newCopy.setChangedCardTypes(in.getChangedCardTypesMap());
newCopy.setChangedCardNames(in.getChangedCardNames()); newCopy.setChangedCardNames(in.getChangedCardNames());
newCopy.setChangedCardTraits(in.getChangedCardTraits());
newCopy.copyChangedTextFrom(in); newCopy.copyChangedTextFrom(in);

View File

@@ -1178,7 +1178,7 @@ public class Player extends GameEntity implements Comparable<Player> {
com.remove(eff); com.remove(eff);
eff.setStaticAbilities(Lists.newArrayList()); eff.setStaticAbilities(Lists.newArrayList());
} }
this.updateZoneForView(com); this.updateZoneForView(com);
} }
@Override @Override

View File

@@ -193,7 +193,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
res.setActiveZone(validHostZones); res.setActiveZone(validHostZones);
res.setLayer(getLayer()); res.setLayer(getLayer());
res.setTemporary(isTemporary());
return res; return res;
} }

View File

@@ -356,31 +356,4 @@ public class ReplacementHandler {
return ret; return ret;
} }
public void cleanUpTemporaryReplacements() {
game.forEachCardInGame(new Visitor<Card>() {
@Override
public boolean visit(Card c) {
List<ReplacementEffect> 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<Card>() {
@Override
public boolean visit(Card c) {
for (int i = 0; i < c.getReplacementEffects().size(); i++) {
c.getReplacementEffects().get(i).setTemporarilySuppressed(false);
}
return true;
}
});
}
} }

View File

@@ -32,6 +32,15 @@ public final class SpellAbilityPredicates extends CardTraitPredicates {
} }
}; };
} }
public static final Predicate<SpellAbility> isManaAbility() {
return new Predicate<SpellAbility>() {
@Override
public boolean apply(final SpellAbility sa) {
return sa.isManaAbility();
}
};
}
public static final Predicate<SpellAbility> isIntrinsic() { public static final Predicate<SpellAbility> isIntrinsic() {
return new Predicate<SpellAbility>() { return new Predicate<SpellAbility>() {

View File

@@ -159,14 +159,14 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
} }
if (hasParam("RemoveAllAbilities") || hasParam("GainsAbilitiesOf")) { if (hasParam("RemoveAllAbilities") || hasParam("GainsAbilitiesOf")) {
layers.add(StaticAbilityLayer.ABILITIES1); layers.add(StaticAbilityLayer.ABILITIES);
} }
if (hasParam("AddKeyword") || hasParam("AddAbility") if (hasParam("AddKeyword") || hasParam("AddAbility")
|| hasParam("AddTrigger") || hasParam("RemoveTriggers") || hasParam("AddTrigger") || hasParam("RemoveTriggers")
|| hasParam("RemoveKeyword") || hasParam("AddReplacementEffects") || hasParam("RemoveKeyword") || hasParam("AddReplacementEffects")
|| hasParam("AddStaticAbility") || hasParam("AddSVar")) { || hasParam("AddStaticAbility") || hasParam("AddSVar")) {
layers.add(StaticAbilityLayer.ABILITIES2); layers.add(StaticAbilityLayer.ABILITIES);
} }
if (hasParam("CharacteristicDefining")) { if (hasParam("CharacteristicDefining")) {
@@ -183,7 +183,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
if (hasParam("AddHiddenKeyword")) { if (hasParam("AddHiddenKeyword")) {
// special rule for can't have or gain // special rule for can't have or gain
if (getParam("AddHiddenKeyword").contains("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); 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) { public final CardCollectionView applyContinuousAbilityBefore(final StaticAbilityLayer layer, final CardCollectionView preList) {
if (!shouldApplyContinuousAbility(layer, false)) { if (!shouldApplyContinuousAbility(layer)) {
return null; return null;
} }
return StaticAbilityContinuous.applyContinuousAbility(this, layer, preList); return StaticAbilityContinuous.applyContinuousAbility(this, layer, preList);
} }
public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer, final CardCollectionView affected) { public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer, final CardCollectionView affected) {
if (!shouldApplyContinuousAbility(layer, true)) { if (!shouldApplyContinuousAbility(layer)) {
return null; return null;
} }
return StaticAbilityContinuous.applyContinuousAbility(this, affected, layer); 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 * affects the specified layer, it's not suppressed, and its
* conditions are fulfilled. * conditions are fulfilled.
*/ */
private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean ignoreTempSuppression) { private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer) {
final boolean isSuppressed; return getParam("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed() && this.checkConditions();
if (ignoreTempSuppression) {
isSuppressed = this.isNonTempSuppressed();
} else {
isSuppressed = this.isSuppressed();
}
return getParam("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed && this.checkConditions();
} }
// apply the ability if it has the right mode // apply the ability if it has the right mode

View File

@@ -122,7 +122,6 @@ public final class StaticAbilityContinuous {
String addColors = null; String addColors = null;
String[] addTriggers = null; String[] addTriggers = null;
String[] addStatics = null; String[] addStatics = null;
List<SpellAbility> addFullAbs = null;
boolean removeAllAbilities = false; boolean removeAllAbilities = false;
boolean removeIntrinsicAbilities = false; boolean removeIntrinsicAbilities = false;
boolean removeNonMana = false; boolean removeNonMana = false;
@@ -187,7 +186,7 @@ public final class StaticAbilityContinuous {
toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb, true); 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(" & "); addKeywords = params.get("AddKeyword").split(" & ");
final Iterable<String> chosencolors = hostCard.getChosenColors(); final Iterable<String> chosencolors = hostCard.getChosenColors();
for (final String color : chosencolors) { 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 // can't have or gain, need to be applyed in ABILITIES1
for (String k : params.get("AddHiddenKeyword").split(" & ")) { 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); addHiddenKeywords.add(k);
} }
} }
if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("RemoveKeyword")) { if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveKeyword")) {
removeKeywords = params.get("RemoveKeyword").split(" & "); removeKeywords = params.get("RemoveKeyword").split(" & ");
} }
if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("RemoveAllAbilities")) { if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("RemoveAllAbilities")) {
removeAllAbilities = true; removeAllAbilities = true;
if (params.containsKey("ExceptManaAbilities")) { if (params.containsKey("ExceptManaAbilities")) {
removeNonMana = true; removeNonMana = true;
} }
} }
// do this in type layer too in case of blood moon // 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")) { && params.containsKey("RemoveIntrinsicAbilities")) {
removeIntrinsicAbilities = true; removeIntrinsicAbilities = true;
} }
if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddAbility")) { if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddAbility")) {
final String[] sVars = params.get("AddAbility").split(" & "); final String[] sVars = params.get("AddAbility").split(" & ");
for (int i = 0; i < sVars.length; i++) { for (int i = 0; i < sVars.length; i++) {
sVars[i] = hostCard.getSVar(sVars[i]); sVars[i] = hostCard.getSVar(sVars[i]);
@@ -254,7 +253,7 @@ public final class StaticAbilityContinuous {
addAbilities = sVars; addAbilities = sVars;
} }
if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddReplacementEffects")) { if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddReplacementEffects")) {
final String[] sVars = params.get("AddReplacementEffects").split(" & "); final String[] sVars = params.get("AddReplacementEffects").split(" & ");
for (int i = 0; i < sVars.length; i++) { for (int i = 0; i < sVars.length; i++) {
sVars[i] = hostCard.getSVar(sVars[i]); sVars[i] = hostCard.getSVar(sVars[i]);
@@ -262,7 +261,7 @@ public final class StaticAbilityContinuous {
addReplacements = sVars; addReplacements = sVars;
} }
if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddSVar")) { if (layer == StaticAbilityLayer.ABILITIES && params.containsKey("AddSVar")) {
addSVars = params.get("AddSVar").split(" & "); 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")) { if (params.containsKey("AddTrigger")) {
final String[] sVars = params.get("AddTrigger").split(" & "); final String[] sVars = params.get("AddTrigger").split(" & ");
for (int i = 0; i < sVars.length; i++) { 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<ZoneType> 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) { if (layer == StaticAbilityLayer.RULES) {
// These fall under Rule changes, as they don't fit any other category // These fall under Rule changes, as they don't fit any other category
if (params.containsKey("MayLookAt")) { if (params.containsKey("MayLookAt")) {
@@ -444,7 +404,6 @@ public final class StaticAbilityContinuous {
if (addStatics != null) { if (addStatics != null) {
for (String s : addStatics) { for (String s : addStatics) {
StaticAbility stat = p.addStaticAbility(hostCard, s); StaticAbility stat = p.addStaticAbility(hostCard, s);
stat.setTemporary(true);
stat.setIntrinsic(false); stat.setIntrinsic(false);
} }
} }
@@ -582,38 +541,117 @@ public final class StaticAbilityContinuous {
} }
} }
if (addFullAbs != null) { if (layer == StaticAbilityLayer.ABILITIES) {
for (final SpellAbility ab : addFullAbs) {
affectedCard.addSpellAbility(ab, false); List<SpellAbility> addedAbilities = Lists.newArrayList();
List<ReplacementEffect> addedReplacementEffects = Lists.newArrayList();
List<Trigger> addedTrigger = Lists.newArrayList();
List<StaticAbility> 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<ZoneType> 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 (layer == StaticAbilityLayer.TYPE && removeIntrinsicAbilities) {
if (addAbilities != null) { affectedCard.addChangedCardTraits(null, null, null, null, null, false, false, removeIntrinsicAbilities, hostCard.getTimestamp());
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);
}
} }
// add Types // add Types
@@ -628,81 +666,6 @@ public final class StaticAbilityContinuous {
affectedCard.addColor(addColors, !overwriteColors, hostCard.getTimestamp()); 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 (layer == StaticAbilityLayer.RULES) {
if (params.containsKey("Goad")) { if (params.containsKey("Goad")) {
affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); affectedCard.addGoad(se.getTimestamp(), hostCard.getController());
@@ -765,11 +728,11 @@ public final class StaticAbilityContinuous {
} }
}; };
addIgnore.setTemporary(true);
addIgnore.setIntrinsic(false); addIgnore.setIntrinsic(false);
addIgnore.setApi(ApiType.InternalIgnoreEffect); addIgnore.setApi(ApiType.InternalIgnoreEffect);
addIgnore.setDescription(cost + " Ignore the effect until end of turn."); 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() { final GameCommand removeIgnore = new GameCommand() {
private static final long serialVersionUID = -5415775215053216360L; private static final long serialVersionUID = -5415775215053216360L;

View File

@@ -18,11 +18,8 @@ public enum StaticAbilityLayer {
/** Layer 5 for color-changing effects. */ /** Layer 5 for color-changing effects. */
COLOR, COLOR,
/** Layer 6 for ability-removing and -copying effects. */ /** Layer 6 for ability effects. */
ABILITIES1, ABILITIES,
/** Layer 6 for ability-granting effects. */
ABILITIES2,
/** Layer 7a for characteristic-defining power/toughness effects. */ /** Layer 7a for characteristic-defining power/toughness effects. */
CHARACTERISTIC, CHARACTERISTIC,
@@ -37,5 +34,5 @@ public enum StaticAbilityLayer {
RULES; RULES;
public final static ImmutableList<StaticAbilityLayer> CONTINUOUS_LAYERS = public final static ImmutableList<StaticAbilityLayer> 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);
} }

View File

@@ -561,7 +561,6 @@ public abstract class Trigger extends TriggerReplacementBase {
copy.setTriggerPhases(Lists.newArrayList(validPhases)); copy.setTriggerPhases(Lists.newArrayList(validPhases));
} }
copy.setActiveZone(validHostZones); copy.setActiveZone(validHostZones);
copy.setTemporary(isTemporary());
return copy; return copy;
} }

View File

@@ -67,42 +67,6 @@ public class TriggerHandler {
} }
public final void cleanUpTemporaryTriggers() { public final void cleanUpTemporaryTriggers() {
game.forEachCardInGame(new Visitor<Card>() {
@Override
public boolean visit(Card c) {
boolean changed = false;
List<Trigger> 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<Card>() {
@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() { public final boolean hasDelayedTriggers() {
@@ -699,9 +663,6 @@ public class TriggerHandler {
Player p = regtrig.getHostCard().getController(); Player p = regtrig.getHostCard().getController();
p.getZone(ZoneType.Command).remove(regtrig.getHostCard()); p.getZone(ZoneType.Command).remove(regtrig.getHostCard());
} }
else {
regtrig.getHostCard().removeTrigger(regtrig);
}
} }
} }