This commit is contained in:
Alessandro Coli
2019-10-06 11:40:00 +02:00
65 changed files with 887 additions and 1069 deletions

View File

@@ -2079,8 +2079,7 @@ public class AiController {
return true; return true;
} }
public ReplacementEffect chooseSingleReplacementEffect(List<ReplacementEffect> list, public ReplacementEffect chooseSingleReplacementEffect(List<ReplacementEffect> list) {
Map<String, Object> runParams) {
// no need to choose anything // no need to choose anything
if (list.size() <= 1) { if (list.size() <= 1) {
return Iterables.getFirst(list, null); return Iterables.getFirst(list, null);

View File

@@ -29,6 +29,7 @@ import forge.card.MagicColor;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
import forge.game.*; import forge.game.*;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
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.ability.effects.CharmEffect; import forge.game.ability.effects.CharmEffect;
@@ -1266,7 +1267,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
} }
@@ -2849,10 +2850,9 @@ public class ComputerUtil {
} }
// Run any applicable replacement effects. // Run any applicable replacement effects.
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(player);
repParams.put("Affected", player); repParams.put(AbilityKey.LifeGained, 1);
repParams.put("LifeGained", 1); repParams.put(AbilityKey.Source, source);
repParams.put("Source", source);
List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList( List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList(
ReplacementType.GainLife, ReplacementType.GainLife,
@@ -2880,15 +2880,15 @@ public class ComputerUtil {
} }
// Run any applicable replacement effects. // Run any applicable replacement effects.
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(player);
repParams.put("Affected", player); repParams.put(AbilityKey.LifeGained, n);
repParams.put("LifeGained", n); repParams.put(AbilityKey.Source, source);
repParams.put("Source", source);
List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList( List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList(
ReplacementType.GainLife, ReplacementType.GainLife,
repParams, repParams,
ReplacementLayer.Other); ReplacementLayer.Other
);
if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "NoLife"))) { if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "NoLife"))) {
// no life gain is not negative // no life gain is not negative

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

@@ -29,6 +29,7 @@ import forge.game.CardTraitBase;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
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.card.*; import forge.game.card.*;
@@ -2580,13 +2581,11 @@ public class ComputerUtilCombat {
final Game game = attacker.getGame(); final Game game = attacker.getGame();
// first try to replace the damage // first try to replace the damage
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(target);
repParams.put("Affected", target); repParams.put(AbilityKey.DamageSource, attacker);
repParams.put("DamageSource", attacker); repParams.put(AbilityKey.DamageAmount, damage);
repParams.put("DamageAmount", damage); repParams.put(AbilityKey.IsCombat, true);
repParams.put("IsCombat", true); repParams.put(AbilityKey.Prevention, true);
repParams.put("Prevention", true);
// repParams.put("PreventMap", preventMap);
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList( List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(
ReplacementType.DamageDone, repParams, ReplacementLayer.Other); ReplacementType.DamageDone, repParams, ReplacementLayer.Other);

View File

@@ -13,6 +13,7 @@ import forge.card.mana.ManaCostParser;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
import forge.game.Game; import forge.game.Game;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
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.card.*; import forge.game.card.*;
@@ -1408,11 +1409,11 @@ public class ComputerUtilMana {
AbilityManaPart mp = m.getManaPart(); AbilityManaPart mp = m.getManaPart();
// setup produce mana replacement effects // setup produce mana replacement effects
final Map<String, Object> repParams = new HashMap<>(); final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put("Mana", mp.getOrigProduced()); repParams.put(AbilityKey.Mana, mp.getOrigProduced());
repParams.put("Affected", sourceCard); repParams.put(AbilityKey.Affected, sourceCard);
repParams.put("Player", ai); repParams.put(AbilityKey.Player, ai);
repParams.put("AbilityMana", m); repParams.put(AbilityKey.AbilityMana, m);
for (final ReplacementEffect replacementEffect : replacementEffects) { for (final ReplacementEffect replacementEffect : replacementEffects) {
if (replacementEffect.canReplace(repParams)) { if (replacementEffect.canReplace(repParams)) {

View File

@@ -866,8 +866,8 @@ public class PlayerControllerAi extends PlayerController {
} }
@Override @Override
public ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, Map<String, Object> runParams) { public ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers) {
return brains.chooseSingleReplacementEffect(possibleReplacers, runParams); return brains.chooseSingleReplacementEffect(possibleReplacers);
} }
@Override @Override

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

@@ -10,6 +10,7 @@ import forge.card.MagicColor;
import forge.game.Game; import forge.game.Game;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;
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.card.*; import forge.game.card.*;
@@ -1789,8 +1790,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
public boolean doReturnCommanderLogic(SpellAbility sa, Player aiPlayer) { public boolean doReturnCommanderLogic(SpellAbility sa, Player aiPlayer) {
Map<String, Object> originalParams = (Map<String, Object>)sa.getReplacingObject("OriginalParams"); Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>)sa.getReplacingObject(AbilityKey.OriginalParams);
SpellAbility causeSa = (SpellAbility)originalParams.get("Cause"); SpellAbility causeSa = (SpellAbility)originalParams.get(AbilityKey.Cause);
SpellAbility causeSub = null; SpellAbility causeSub = null;
// Squee, the Immortal: easier to recast it (the call below has to be "contains" since SA is an intrinsic effect) // Squee, the Immortal: easier to recast it (the call below has to be "contains" since SA is an intrinsic effect)
@@ -1813,7 +1814,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
// A blink effect implemented using a delayed trigger // A blink effect implemented using a delayed trigger
return !"Exile".equals(exec.getParam("Origin")) || !"Battlefield".equals(exec.getParam("Destination")); return !"Exile".equals(exec.getParam("Origin")) || !"Battlefield".equals(exec.getParam("Destination"));
} }
} else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject("Card")) } else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject(AbilityKey.Card))
|| !causeSa.getActivatingPlayer().equals(aiPlayer); || !causeSa.getActivatingPlayer().equals(aiPlayer);
} }

View File

@@ -2,12 +2,12 @@ package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
@@ -97,11 +97,10 @@ public class ManifestAi extends SpellAbilityAi {
topCopy.turnFaceDownNoUpdate(); topCopy.turnFaceDownNoUpdate();
topCopy.setManifested(true); topCopy.setManifested(true);
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(topCopy);
repParams.put("Affected", topCopy); repParams.put(AbilityKey.Origin, card.getZone().getZoneType());
repParams.put("Origin", card.getZone().getZoneType()); repParams.put(AbilityKey.Destination, ZoneType.Battlefield);
repParams.put("Destination", ZoneType.Battlefield); repParams.put(AbilityKey.Source, sa.getHostCard());
repParams.put("Source", sa.getHostCard());
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.Other); List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.Other);
if (!list.isEmpty()) { if (!list.isEmpty()) {
return false; return false;

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

@@ -55,8 +55,6 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.*; import java.util.*;
import static forge.util.EnumMapUtil.toStringMap;
/** /**
* Methods for common actions performed during a game. * Methods for common actions performed during a game.
* *
@@ -148,7 +146,7 @@ public class GameAction {
} }
// if an adventureCard is put from Stack somewhere else, need to reset to Original State // if an adventureCard is put from Stack somewhere else, need to reset to Original State
if (c.isAdventureCard() && (ZoneType.Stack.equals(zoneFrom) || !ZoneType.Stack.equals(zoneTo))) { //fix NPE momir if (c.isAdventureCard() && ((zoneFrom != null && zoneFrom.is(ZoneType.Stack)) || !zoneTo.is(ZoneType.Stack))) {
c.setState(CardStateName.Original, true); c.setState(CardStateName.Original, true);
} }
@@ -292,8 +290,7 @@ public class GameAction {
copied.getOwner().addInboundToken(copied); copied.getOwner().addInboundToken(copied);
} }
Map<AbilityKey, Object> repParams = AbilityKey.newMap(); Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(copied);
repParams.put(AbilityKey.Affected, copied);
repParams.put(AbilityKey.CardLKI, lastKnownInfo); repParams.put(AbilityKey.CardLKI, lastKnownInfo);
repParams.put(AbilityKey.Cause, cause); repParams.put(AbilityKey.Cause, cause);
repParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType() : null); repParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType() : null);
@@ -303,7 +300,7 @@ public class GameAction {
repParams.putAll(params); repParams.putAll(params);
} }
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, toStringMap(repParams)); ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
if (repres != ReplacementResult.NotReplaced) { if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original // reset failed manifested Cards back to original
if (c.isManifested()) { if (c.isManifested()) {
@@ -760,8 +757,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 +774,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);
} }
@@ -1397,11 +1386,10 @@ public class GameAction {
} }
// Replacement effects // Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromCard(c);
repRunParams.put("Source", sa); repRunParams.put(AbilityKey.Source, sa);
repRunParams.put("Card", c); repRunParams.put(AbilityKey.Affected, c);
repRunParams.put("Affected", c); repRunParams.put(AbilityKey.Regeneration, regenerate);
repRunParams.put("Regeneration", regenerate);
if (game.getReplacementHandler().run(ReplacementType.Destroy, repRunParams) != ReplacementResult.NotReplaced) { if (game.getReplacementHandler().run(ReplacementType.Destroy, repRunParams) != ReplacementResult.NotReplaced) {
return false; return false;

View File

@@ -115,25 +115,24 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
public int replaceDamage(final int damage, final Card source, final boolean isCombat, final boolean prevention, public int replaceDamage(final int damage, final Card source, final boolean isCombat, final boolean prevention,
final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) {
// Replacement effects // Replacement effects
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.DamageSource, source);
repParams.put("DamageSource", source); repParams.put(AbilityKey.DamageAmount, damage);
repParams.put("DamageAmount", damage); repParams.put(AbilityKey.IsCombat, isCombat);
repParams.put("IsCombat", isCombat); repParams.put(AbilityKey.NoPreventDamage, !prevention);
repParams.put("NoPreventDamage", !prevention); repParams.put(AbilityKey.DamageMap, damageMap);
repParams.put("DamageMap", damageMap); repParams.put(AbilityKey.PreventMap, preventMap);
repParams.put("PreventMap", preventMap); repParams.put(AbilityKey.CounterTable, counterTable);
repParams.put("CounterTable", counterTable);
if (cause != null) { if (cause != null) {
repParams.put("Cause", cause); repParams.put(AbilityKey.Cause, cause);
} }
switch (getGame().getReplacementHandler().run(ReplacementType.DamageDone, repParams)) { switch (getGame().getReplacementHandler().run(ReplacementType.DamageDone, repParams)) {
case NotReplaced: case NotReplaced:
return damage; return damage;
case Updated: case Updated:
int newDamage = (int) repParams.get("DamageAmount"); int newDamage = (int) repParams.get(AbilityKey.DamageAmount);
GameEntity newTarget = (GameEntity) repParams.get("Affected"); GameEntity newTarget = (GameEntity) repParams.get(AbilityKey.Affected);
// check if this is still the affected card or player // check if this is still the affected card or player
if (this.equals(newTarget)) { if (this.equals(newTarget)) {
return newDamage; return newDamage;
@@ -169,15 +168,14 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
int restDamage = damage; int restDamage = damage;
// first try to replace the damage // first try to replace the damage
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.DamageSource, source);
repParams.put("DamageSource", source); repParams.put(AbilityKey.DamageAmount, damage);
repParams.put("DamageAmount", damage); repParams.put(AbilityKey.IsCombat, isCombat);
repParams.put("IsCombat", isCombat); repParams.put(AbilityKey.Prevention, true);
repParams.put("Prevention", true); repParams.put(AbilityKey.PreventMap, preventMap);
repParams.put("PreventMap", preventMap);
if (cause != null) { if (cause != null) {
repParams.put("Cause", cause); repParams.put(AbilityKey.Cause, cause);
} }
switch (getGame().getReplacementHandler().run(ReplacementType.DamageDone, repParams)) { switch (getGame().getReplacementHandler().run(ReplacementType.DamageDone, repParams)) {
@@ -185,7 +183,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
restDamage = damage; restDamage = damage;
break; break;
case Updated: case Updated:
restDamage = (int) repParams.get("DamageAmount"); restDamage = (int) repParams.get(AbilityKey.DamageAmount);
break; break;
default: default:
restDamage = 0; restDamage = 0;

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

@@ -3,6 +3,7 @@ package forge.game.ability;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import forge.game.GameEntity;
import forge.game.card.Card; import forge.game.card.Card;
/** /**
@@ -37,12 +38,15 @@ public enum AbilityKey {
CostStack("CostStack"), CostStack("CostStack"),
CounterAmount("CounterAmount"), CounterAmount("CounterAmount"),
CounteredSA("CounteredSA"), CounteredSA("CounteredSA"),
CounterNum("CounterNum"),
CounterTable("CounterTable"),
CounterType("CounterType"), CounterType("CounterType"),
Crew("Crew"), Crew("Crew"),
CumulativeUpkeepPaid("CumulativeUpkeepPaid"), CumulativeUpkeepPaid("CumulativeUpkeepPaid"),
CurrentCastSpells("CurrentCastSpells"), CurrentCastSpells("CurrentCastSpells"),
CurrentStormCount("CurrentStormCount"), CurrentStormCount("CurrentStormCount"),
DamageAmount("DamageAmount"), DamageAmount("DamageAmount"),
DamageMap("DamageMap"),
DamageSource("DamageSource"), DamageSource("DamageSource"),
DamageSources("DamageSources"), DamageSources("DamageSources"),
DamageTarget("DamageTarget"), DamageTarget("DamageTarget"),
@@ -53,18 +57,23 @@ public enum AbilityKey {
Destination("Destination"), Destination("Destination"),
Devoured("Devoured"), Devoured("Devoured"),
EchoPaid("EchoPaid"), EchoPaid("EchoPaid"),
EffectOnly("EffectOnly"),
Exploited("Exploited"), Exploited("Exploited"),
Explorer("Explorer"), Explorer("Explorer"),
Event("Event"), Event("Event"),
Fighter("Fighter"), Fighter("Fighter"),
FirstTime("FirstTime"), FirstTime("FirstTime"),
Fizzle("Fizzle"), Fizzle("Fizzle"),
IsCombat("IsCombat"), // TODO confirm that this and IsCombatDamage can be merged
IsCombatDamage("IsCombatDamage"), IsCombatDamage("IsCombatDamage"),
IndividualCostPaymentInstance("IndividualCostPaymentInstance"), IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
IsMadness("IsMadness"), IsMadness("IsMadness"),
LifeAmount("LifeAmount"), LifeAmount("LifeAmount"), //TODO confirm that this and LifeGained can be merged
LifeGained("LifeGained"),
Mana("Mana"),
MonstrosityAmount("MonstrosityAmount"), MonstrosityAmount("MonstrosityAmount"),
NewCounterAmount("NewCounterAmount"), NewCounterAmount("NewCounterAmount"),
NoPreventDamage("NoPreventDamage"),
Num("Num"), // TODO confirm that this and NumThisTurn can be merged Num("Num"), // TODO confirm that this and NumThisTurn can be merged
NumBlockers("NumBlockers"), NumBlockers("NumBlockers"),
NumThisTurn("NumThisTurn"), NumThisTurn("NumThisTurn"),
@@ -76,10 +85,15 @@ public enum AbilityKey {
Origin("Origin"), Origin("Origin"),
OriginalController("OriginalController"), OriginalController("OriginalController"),
OriginalDefender("OriginalDefender"), OriginalDefender("OriginalDefender"),
OriginalParams("OriginalParams"),
PayingMana("PayingMana"), PayingMana("PayingMana"),
Phase("Phase"), Phase("Phase"),
Player("Player"), Player("Player"),
PreventMap("PreventMap"),
Prevention("Prevention"),
Produced("Produced"), Produced("Produced"),
Regeneration("Regeneration"),
ReplacementResult("ReplacementResult"),
Result("Result"), Result("Result"),
Scheme("Scheme"), Scheme("Scheme"),
Source("Source"), Source("Source"),
@@ -91,8 +105,12 @@ public enum AbilityKey {
StackInstance("StackInstance"), StackInstance("StackInstance"),
StackSa("StackSa"), StackSa("StackSa"),
StackSi("StackSi"), StackSi("StackSi"),
SurveilNum("SurveilNum"),
Target("Target"), Target("Target"),
Targets("Targets"), Targets("Targets"),
TgtSA("TgtSA"),
Token("Token"),
TokenNum("TokenNum"),
Transformer("Transformer"), Transformer("Transformer"),
Vehicle("Vehicle"), Vehicle("Vehicle"),
Won("Won"); Won("Won");
@@ -141,4 +159,11 @@ public enum AbilityKey {
runParams.put(Card, card); runParams.put(Card, card);
return runParams; return runParams;
} }
public static Map<AbilityKey, Object> mapFromAffected(GameEntity gameEntity) {
final Map<AbilityKey, Object> runParams = newMap();
runParams.put(Affected, gameEntity);
return runParams;
}
} }

View File

@@ -48,7 +48,7 @@ public class AbilityUtils {
public static CounterType getCounterType(String name, SpellAbility sa) throws Exception { public static CounterType getCounterType(String name, SpellAbility sa) throws Exception {
CounterType counterType; CounterType counterType;
if ("ReplacedCounterType".equals(name)) { if ("ReplacedCounterType".equals(name)) {
name = (String) sa.getReplacingObject("CounterType"); name = (String) sa.getReplacingObject(AbilityKey.CounterType);
} }
try { try {
counterType = CounterType.getType(name); counterType = CounterType.getType(name);
@@ -157,7 +157,9 @@ public class AbilityUtils {
} }
else if (defined.startsWith("Replaced") && (sa != null)) { else if (defined.startsWith("Replaced") && (sa != null)) {
final SpellAbility root = sa.getRootAbility(); final SpellAbility root = sa.getRootAbility();
final Object crd = root.getReplacingObject(defined.substring(8)); AbilityKey type = AbilityKey.fromString(defined.substring(8));
final Object crd = root.getReplacingObject(type);
if (crd instanceof Card) { if (crd instanceof Card) {
c = game.getCardState((Card) crd); c = game.getCardState((Card) crd);
} else if (crd instanceof List<?>) { } else if (crd instanceof List<?>) {
@@ -712,7 +714,7 @@ public class AbilityUtils {
} }
else if (calcX[0].startsWith("Replaced")) { else if (calcX[0].startsWith("Replaced")) {
final SpellAbility root = sa.getRootAbility(); final SpellAbility root = sa.getRootAbility();
list = new CardCollection((Card) root.getReplacingObject(calcX[0].substring(8))); list = new CardCollection((Card) root.getReplacingObject(AbilityKey.fromString(calcX[0].substring(8))));
} }
else if (calcX[0].startsWith("ReplaceCount")) { else if (calcX[0].startsWith("ReplaceCount")) {
// ReplaceCount is similar to a regular Count, but just // ReplaceCount is similar to a regular Count, but just
@@ -720,7 +722,7 @@ public class AbilityUtils {
final SpellAbility root = sa.getRootAbility(); final SpellAbility root = sa.getRootAbility();
final String[] l = calcX[1].split("/"); final String[] l = calcX[1].split("/");
final String m = CardFactoryUtil.extractOperators(calcX[1]); final String m = CardFactoryUtil.extractOperators(calcX[1]);
final int count = (Integer) root.getReplacingObject(l[0]); final int count = (Integer) root.getReplacingObject(AbilityKey.fromString(l[0]));
return CardFactoryUtil.doXMath(count, m, card) * multiplier; return CardFactoryUtil.doXMath(count, m, card) * multiplier;
} }
@@ -1063,7 +1065,7 @@ public class AbilityUtils {
if (defined.endsWith("Controller")) { if (defined.endsWith("Controller")) {
String replacingType = defined.substring(8); String replacingType = defined.substring(8);
replacingType = replacingType.substring(0, replacingType.length() - 10); replacingType = replacingType.substring(0, replacingType.length() - 10);
final Object c = root.getReplacingObject(replacingType); final Object c = root.getReplacingObject(AbilityKey.fromString(replacingType));
if (c instanceof Card) { if (c instanceof Card) {
o = ((Card) c).getController(); o = ((Card) c).getController();
} }
@@ -1074,14 +1076,14 @@ public class AbilityUtils {
else if (defined.endsWith("Owner")) { else if (defined.endsWith("Owner")) {
String replacingType = defined.substring(8); String replacingType = defined.substring(8);
replacingType = replacingType.substring(0, replacingType.length() - 5); replacingType = replacingType.substring(0, replacingType.length() - 5);
final Object c = root.getReplacingObject(replacingType); final Object c = root.getReplacingObject(AbilityKey.fromString(replacingType));
if (c instanceof Card) { if (c instanceof Card) {
o = ((Card) c).getOwner(); o = ((Card) c).getOwner();
} }
} }
else { else {
final String replacingType = defined.substring(8); final String replacingType = defined.substring(8);
o = root.getReplacingObject(replacingType); o = root.getReplacingObject(AbilityKey.fromString(replacingType));
} }
if (o != null) { if (o != null) {
if (o instanceof Player) { if (o instanceof Player) {

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

@@ -18,7 +18,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class CounterEffect extends SpellAbilityEffect { public class CounterEffect extends SpellAbilityEffect {
@Override @Override
@@ -155,10 +154,9 @@ public class CounterEffect extends SpellAbilityEffect {
final SpellAbility srcSA, final SpellAbilityStackInstance si) { final SpellAbility srcSA, final SpellAbilityStackInstance si) {
final Game game = tgtSA.getActivatingPlayer().getGame(); final Game game = tgtSA.getActivatingPlayer().getGame();
// Run any applicable replacement effects. // Run any applicable replacement effects.
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard());
repParams.put("TgtSA", tgtSA); repParams.put(AbilityKey.TgtSA, tgtSA);
repParams.put("Affected", tgtSA.getHostCard()); repParams.put(AbilityKey.Cause, srcSA.getHostCard());
repParams.put("Cause", srcSA.getHostCard());
if (game.getReplacementHandler().run(ReplacementType.Counter, repParams) != ReplacementResult.NotReplaced) { if (game.getReplacementHandler().run(ReplacementType.Counter, repParams) != ReplacementResult.NotReplaced) {
return; return;
} }

View File

@@ -1,5 +1,6 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -11,6 +12,6 @@ import forge.game.spellability.SpellAbility;
public class ETBReplacementEffect extends SpellAbilityEffect { public class ETBReplacementEffect extends SpellAbilityEffect {
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
sa.getActivatingPlayer().getGame().getAction().moveToPlay(((Card) sa.getReplacingObject("Card")), sa); sa.getActivatingPlayer().getGame().getAction().moveToPlay(((Card) sa.getReplacingObject(AbilityKey.Card)), sa);
} }
} }

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

@@ -2,10 +2,9 @@ package forge.game.ability.effects;
import java.util.Map; import java.util.Map;
import forge.game.ability.AbilityKey;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
@@ -31,10 +30,10 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
String varValue = sa.getParamOrDefault("VarName", "1"); String varValue = sa.getParamOrDefault("VarName", "1");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> originalParams = (Map<String, Object>) sa.getReplacingObject("OriginalParams"); Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
Map<String, Object> params = Maps.newHashMap(originalParams); Map<AbilityKey, Object> params = AbilityKey.newMap(originalParams);
Integer dmg = (Integer) sa.getReplacingObject("DamageAmount"); Integer dmg = (Integer) sa.getReplacingObject(AbilityKey.DamageAmount);
int prevent = AbilityUtils.calculateAmount(card, varValue, sa); int prevent = AbilityUtils.calculateAmount(card, varValue, sa);
@@ -54,10 +53,10 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
// no damage for original target anymore // no damage for original target anymore
if (dmg <= 0) { if (dmg <= 0) {
originalParams.put("ReplacementResult", ReplacementResult.Replaced); originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Replaced);
return; return;
} }
params.put("DamageAmount", dmg); params.put(AbilityKey.DamageAmount, dmg);
//try to call replacementHandler with new Params //try to call replacementHandler with new Params
@@ -65,16 +64,16 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
switch (result) { switch (result) {
case NotReplaced: case NotReplaced:
case Updated: { case Updated: {
for (Map.Entry<String, Object> e : params.entrySet()) { for (Map.Entry<AbilityKey, Object> e : params.entrySet()) {
originalParams.put(e.getKey(), e.getValue()); originalParams.put(e.getKey(), e.getValue());
} }
// effect was updated // effect was updated
originalParams.put("ReplacementResult", ReplacementResult.Updated); originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Updated);
break; break;
} }
default: default:
// effect was replaced with something else // effect was replaced with something else
originalParams.put("ReplacementResult", result); originalParams.put(AbilityKey.ReplacementResult, result);
break; break;
} }
} }

View File

@@ -7,6 +7,7 @@ import com.google.common.collect.Maps;
import forge.game.Game; import forge.game.Game;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
@@ -23,14 +24,14 @@ public class ReplaceEffect extends SpellAbilityEffect {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final Game game = card.getGame(); final Game game = card.getGame();
final String varName = sa.getParam("VarName"); final AbilityKey varName = AbilityKey.fromString(sa.getParam("VarName"));
final String varValue = sa.getParam("VarValue"); final String varValue = sa.getParam("VarValue");
final String type = sa.getParamOrDefault("VarType", "amount"); final String type = sa.getParamOrDefault("VarType", "amount");
final ReplacementType retype = sa.getReplacementEffect().getMode(); final ReplacementType retype = sa.getReplacementEffect().getMode();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> originalParams = (Map<String, Object>) sa.getReplacingObject("OriginalParams"); Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
Map<String, Object> params = Maps.newHashMap(originalParams); Map<AbilityKey, Object> params = Maps.newHashMap(originalParams);
if ("Card".equals(type)) { if ("Card".equals(type)) {
List<Card> list = AbilityUtils.getDefinedCards(card, varValue, sa); List<Card> list = AbilityUtils.getDefinedCards(card, varValue, sa);
@@ -56,8 +57,8 @@ public class ReplaceEffect extends SpellAbilityEffect {
params.put(varName, AbilityUtils.calculateAmount(card, varValue, sa)); params.put(varName, AbilityUtils.calculateAmount(card, varValue, sa));
} }
if (params.containsKey("EffectOnly")) { if (params.containsKey(AbilityKey.EffectOnly)) {
params.put("EffectOnly", true); params.put(AbilityKey.EffectOnly, true);
} }
//try to call replacementHandler with new Params //try to call replacementHandler with new Params
@@ -65,16 +66,16 @@ public class ReplaceEffect extends SpellAbilityEffect {
switch (result) { switch (result) {
case NotReplaced: case NotReplaced:
case Updated: { case Updated: {
for (Map.Entry<String, Object> e : params.entrySet()) { for (Map.Entry<AbilityKey, Object> e : params.entrySet()) {
originalParams.put(e.getKey(), e.getValue()); originalParams.put(e.getKey(), e.getValue());
} }
// effect was updated // effect was updated
originalParams.put("ReplacementResult", ReplacementResult.Updated); originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Updated);
break; break;
} }
default: default:
// effect was replaced with something else // effect was replaced with something else
originalParams.put("ReplacementResult", result); originalParams.put(AbilityKey.ReplacementResult, result);
break; break;
} }
} }

View File

@@ -3,10 +3,9 @@ package forge.game.ability.effects;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import forge.game.ability.AbilityKey;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GameEntityCounterTable; import forge.game.GameEntityCounterTable;
@@ -36,10 +35,10 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
String varValue = sa.getParamOrDefault("VarName", "1"); String varValue = sa.getParamOrDefault("VarName", "1");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> originalParams = (Map<String, Object>) sa.getReplacingObject("OriginalParams"); Map<AbilityKey, Object> originalParams = (Map<AbilityKey , Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
Map<String, Object> params = Maps.newHashMap(originalParams); Map<AbilityKey, Object> params = AbilityKey.newMap(originalParams);
Integer dmg = (Integer) sa.getReplacingObject("DamageAmount"); Integer dmg = (Integer) sa.getReplacingObject(AbilityKey.DamageAmount);
int prevent = AbilityUtils.calculateAmount(card, varValue, sa); int prevent = AbilityUtils.calculateAmount(card, varValue, sa);
@@ -57,15 +56,15 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
card.setSVar(varValue, "Number$" + prevent); card.setSVar(varValue, "Number$" + prevent);
} }
Card sourceLKI = (Card) sa.getReplacingObject("Source"); Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);
CardDamageMap damageMap = (CardDamageMap) originalParams.get("DamageMap"); CardDamageMap damageMap = (CardDamageMap) originalParams.get(AbilityKey.DamageMap);
CardDamageMap preventMap = (CardDamageMap) originalParams.get("PreventMap"); CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap);
GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get("CounterTable"); GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get(AbilityKey.CounterTable);
SpellAbility cause = (SpellAbility) originalParams.get("Cause"); SpellAbility cause = (SpellAbility) originalParams.get(AbilityKey.Cause);
boolean isCombat = (Boolean) originalParams.get("IsCombat"); boolean isCombat = (Boolean) originalParams.get(AbilityKey.IsCombat);
boolean noPrevention = (Boolean) originalParams.get("NoPreventDamage"); boolean noPrevention = (Boolean) originalParams.get(AbilityKey.NoPreventDamage);
GameEntity obj = (GameEntity) list.get(0); GameEntity obj = (GameEntity) list.get(0);
@@ -74,26 +73,26 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
// no damage for original target anymore // no damage for original target anymore
if (dmg <= 0) { if (dmg <= 0) {
originalParams.put("ReplacementResult", ReplacementResult.Replaced); originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Replaced);
return; return;
} }
params.put("DamageAmount", dmg); params.put(AbilityKey.DamageAmount, dmg);
//try to call replacementHandler with new Params //try to call replacementHandler with new Params
ReplacementResult result = game.getReplacementHandler().run(event, params); ReplacementResult result = game.getReplacementHandler().run(event, params);
switch (result) { switch (result) {
case NotReplaced: case NotReplaced:
case Updated: { case Updated: {
for (Map.Entry<String, Object> e : params.entrySet()) { for (Map.Entry<AbilityKey, Object> e : params.entrySet()) {
originalParams.put(e.getKey(), e.getValue()); originalParams.put(e.getKey(), e.getValue());
} }
// effect was updated // effect was updated
originalParams.put("ReplacementResult", ReplacementResult.Updated); originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Updated);
break; break;
} }
default: default:
// effect was replaced with something else // effect was replaced with something else
originalParams.put("ReplacementResult", result); originalParams.put(AbilityKey.ReplacementResult, result);
break; break;
} }
} }

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

@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.game.ability.AbilityKey;
import forge.game.card.token.TokenInfo; import forge.game.card.token.TokenInfo;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -224,8 +225,11 @@ public class TokenEffect extends SpellAbilityEffect {
// Cause of the Token Effect, in general it should be this // Cause of the Token Effect, in general it should be this
// but if its a Replacement Effect, it might be something else or null // but if its a Replacement Effect, it might be something else or null
SpellAbility cause = sa; SpellAbility cause = sa;
if (root.isReplacementAbility() && root.hasReplacingObject("Cause")) { if (root.isReplacementAbility()) {
cause = (SpellAbility)root.getReplacingObject("Cause"); Object replacingObject = root.getReplacingObject(AbilityKey.Cause);
if (replacingObject != null) {
cause = (SpellAbility) replacingObject;
}
} }
final boolean remember = sa.hasParam("RememberTokens"); final boolean remember = sa.hasParam("RememberTokens");

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();
@@ -687,9 +689,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (result && runTriggers) { if (result && runTriggers) {
// Run replacement effects // Run replacement effects
Map<String, Object> repParams = Maps.newHashMap(); getGame().getReplacementHandler().run(ReplacementType.TurnFaceUp, AbilityKey.mapFromAffected(this));
repParams.put("Affected", this);
getGame().getReplacementHandler().run(ReplacementType.TurnFaceUp, repParams);
// Run triggers // Run triggers
getGame().getTriggerHandler().registerActiveTrigger(this, false); getGame().getTriggerHandler().registerActiveTrigger(this, false);
@@ -1012,28 +1012,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 +1037,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());
} }
@@ -1233,18 +1239,17 @@ public class Card extends GameEntity implements Comparable<Card> {
addAmount = 0; // As per rule 107.1b addAmount = 0; // As per rule 107.1b
return 0; return 0;
} }
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.Source, source);
repParams.put("Source", source); repParams.put(AbilityKey.CounterType, counterType);
repParams.put("CounterType", counterType); repParams.put(AbilityKey.CounterNum, addAmount);
repParams.put("CounterNum", addAmount); repParams.put(AbilityKey.EffectOnly, applyMultiplier);
repParams.put("EffectOnly", applyMultiplier);
switch (getGame().getReplacementHandler().run(ReplacementType.AddCounter, repParams)) { switch (getGame().getReplacementHandler().run(ReplacementType.AddCounter, repParams)) {
case NotReplaced: case NotReplaced:
break; break;
case Updated: { case Updated: {
addAmount = (int) repParams.get("CounterNum"); addAmount = (int) repParams.get(AbilityKey.CounterNum);
break; break;
} }
default: default:
@@ -2379,10 +2384,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 +2415,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 +2765,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) {
@@ -3572,10 +3608,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (!tapped) { return; } if (!tapped) { return; }
// Run Replacement effects // Run Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); if (getGame().getReplacementHandler().run(ReplacementType.Untap, AbilityKey.mapFromAffected(this)) != ReplacementResult.NotReplaced) {
repRunParams.put("Affected", this);
if (getGame().getReplacementHandler().run(ReplacementType.Untap, repRunParams) != ReplacementResult.NotReplaced) {
return; return;
} }
@@ -3589,6 +3622,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 +4076,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 +5719,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 +6385,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

@@ -3,12 +3,12 @@ package forge.game.card.token;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ImageKeys; import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.card.CardType; import forge.card.CardType;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactory; import forge.game.card.CardFactory;
@@ -166,19 +166,18 @@ public class TokenInfo {
Player player = controller; Player player = controller;
Card proto = prototype; Card proto = prototype;
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(player);
repParams.put("Affected", player); repParams.put(AbilityKey.Token, prototype);
repParams.put("Token", prototype); repParams.put(AbilityKey.TokenNum, multiplier);
repParams.put("TokenNum", multiplier); repParams.put(AbilityKey.EffectOnly, applyMultiplier);
repParams.put("EffectOnly", applyMultiplier);
switch (game.getReplacementHandler().run(ReplacementType.CreateToken, repParams)) { switch (game.getReplacementHandler().run(ReplacementType.CreateToken, repParams)) {
case NotReplaced: case NotReplaced:
break; break;
case Updated: { case Updated: {
multiplier = (int) repParams.get("TokenNum"); multiplier = (int) repParams.get(AbilityKey.TokenNum);
player = (Player) repParams.get("Affected"); player = (Player) repParams.get(AbilityKey.Affected);
proto = (Card) repParams.get("Token"); proto = (Card) repParams.get(AbilityKey.Token);
break; break;
} }
default: default:

View File

@@ -235,10 +235,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
// Replacement effects // Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); if (game.getReplacementHandler().run(ReplacementType.SetInMotion, AbilityKey.mapFromAffected(this)) != ReplacementResult.NotReplaced) {
repRunParams.put("Affected", this);
if (game.getReplacementHandler().run(ReplacementType.SetInMotion, repRunParams) != ReplacementResult.NotReplaced) {
return; return;
} }
@@ -403,10 +400,9 @@ public class Player extends GameEntity implements Comparable<Player> {
public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) { public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) {
// Run any applicable replacement effects. // Run any applicable replacement effects.
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.LifeGained, lifeGain);
repParams.put("LifeGained", lifeGain); repParams.put(AbilityKey.Source, source);
repParams.put("Source", source);
if (!canGainLife()) { if (!canGainLife()) {
return false; return false;
@@ -417,8 +413,8 @@ public class Player extends GameEntity implements Comparable<Player> {
break; break;
case Updated: case Updated:
// check if this is still the affected player // check if this is still the affected player
if (this.equals(repParams.get("Affected"))) { if (this.equals(repParams.get(AbilityKey.Affected))) {
lifeGain = (int) repParams.get("LifeGained"); lifeGain = (int) repParams.get(AbilityKey.LifeGained);
// negative update means life loss // negative update means life loss
if (lifeGain < 0) { if (lifeGain < 0) {
this.loseLife(-lifeGain); this.loseLife(-lifeGain);
@@ -914,18 +910,17 @@ public class Player extends GameEntity implements Comparable<Player> {
return 0; return 0;
} }
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.Source, source);
repParams.put("Source", source); repParams.put(AbilityKey.CounterType, counterType);
repParams.put("CounterType", counterType); repParams.put(AbilityKey.CounterNum, addAmount);
repParams.put("CounterNum", addAmount); repParams.put(AbilityKey.EffectOnly, applyMultiplier);
repParams.put("EffectOnly", applyMultiplier);
switch (getGame().getReplacementHandler().run(ReplacementType.AddCounter, repParams)) { switch (getGame().getReplacementHandler().run(ReplacementType.AddCounter, repParams)) {
case NotReplaced: case NotReplaced:
break; break;
case Updated: { case Updated: {
addAmount = (int) repParams.get("CounterNum"); addAmount = (int) repParams.get(AbilityKey.CounterNum);
break; break;
} }
default: default:
@@ -1178,7 +1173,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
@@ -1276,16 +1271,15 @@ public class Player extends GameEntity implements Comparable<Player> {
public void surveil(int num, SpellAbility cause) { public void surveil(int num, SpellAbility cause) {
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
repParams.put("Affected", this); repParams.put(AbilityKey.Source, cause);
repParams.put("Source", cause); repParams.put(AbilityKey.SurveilNum, num);
repParams.put("SurveilNum", num);
switch (getGame().getReplacementHandler().run(ReplacementType.Surveil, repParams)) { switch (getGame().getReplacementHandler().run(ReplacementType.Surveil, repParams)) {
case NotReplaced: case NotReplaced:
break; break;
case Updated: { case Updated: {
num = (int) repParams.get("SurveilNum"); num = (int) repParams.get(AbilityKey.SurveilNum);
break; break;
} }
default: default:
@@ -1346,9 +1340,8 @@ public class Player extends GameEntity implements Comparable<Player> {
final CardCollection toReveal = new CardCollection(); final CardCollection toReveal = new CardCollection();
// Replacement effects // Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(this);
repRunParams.put("Affected", this); repRunParams.put(AbilityKey.Number, n);
repRunParams.put("Number", n);
if (game.getReplacementHandler().run(ReplacementType.DrawCards, repRunParams) != ReplacementResult.NotReplaced) { if (game.getReplacementHandler().run(ReplacementType.DrawCards, repRunParams) != ReplacementResult.NotReplaced) {
return drawn; return drawn;
@@ -1378,10 +1371,7 @@ public class Player extends GameEntity implements Comparable<Player> {
final PlayerZone library = getZone(ZoneType.Library); final PlayerZone library = getZone(ZoneType.Library);
// Replacement effects // Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); if (game.getReplacementHandler().run(ReplacementType.Draw, AbilityKey.mapFromAffected(this)) != ReplacementResult.NotReplaced) {
repRunParams.put("Affected", this);
if (game.getReplacementHandler().run(ReplacementType.Draw, repRunParams) != ReplacementResult.NotReplaced) {
return drawn; return drawn;
} }
@@ -1556,10 +1546,9 @@ public class Player extends GameEntity implements Comparable<Player> {
// that should not trigger other Replacement again // that should not trigger other Replacement again
if (!discardToTopOfLibrary && !discardMadness) { if (!discardToTopOfLibrary && !discardMadness) {
// Replacement effects // Replacement effects
final Map<String, Object> repRunParams = Maps.newHashMap(); final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromCard(c);
repRunParams.put("Card", c); repRunParams.put(AbilityKey.Source, source);
repRunParams.put("Source", source); repRunParams.put(AbilityKey.Affected, this);
repRunParams.put("Affected", this);
if (game.getReplacementHandler().run(ReplacementType.Discard, repRunParams) != ReplacementResult.NotReplaced) { if (game.getReplacementHandler().run(ReplacementType.Discard, repRunParams) != ReplacementResult.NotReplaced) {
return null; return null;
@@ -1861,10 +1850,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
// Replacement effects // Replacement effects
final Map<String, Object> runParams = Maps.newHashMap(); if (game.getReplacementHandler().run(ReplacementType.GameLoss, AbilityKey.mapFromAffected(this)) != ReplacementResult.NotReplaced) {
runParams.put("Affected", this);
if (game.getReplacementHandler().run(ReplacementType.GameLoss, runParams) != ReplacementResult.NotReplaced) {
return false; return false;
} }
} }

View File

@@ -211,7 +211,7 @@ public abstract class PlayerController {
Map<String, Object> params); Map<String, Object> params);
public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa); public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa);
public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, Map<String, Object> runParams); public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers);
public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices); public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices);
// these 4 need some refining. // these 4 need some refining.

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CounterType; import forge.game.card.CounterType;
import forge.game.player.Player; import forge.game.player.Player;
@@ -27,20 +28,20 @@ public class ReplaceAddCounter extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (((int) runParams.get("CounterNum")) <= 0) { if (((int) runParams.get(AbilityKey.CounterNum)) <= 0) {
return false; return false;
} }
if (hasParam("EffectOnly")) { if (hasParam("EffectOnly")) {
final Boolean effectOnly = (Boolean) runParams.get("EffectOnly"); final Boolean effectOnly = (Boolean) runParams.get(AbilityKey.EffectOnly);
if (!effectOnly) { if (!effectOnly) {
return false; return false;
} }
} }
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
Object o = runParams.get("Affected"); Object o = runParams.get(AbilityKey.Affected);
if (!(o instanceof Card)) { if (!(o instanceof Card)) {
return false; return false;
} }
@@ -48,7 +49,7 @@ public class ReplaceAddCounter extends ReplacementEffect {
return false; return false;
} }
} else if (hasParam("ValidPlayer")) { } else if (hasParam("ValidPlayer")) {
Object o = runParams.get("Affected"); Object o = runParams.get(AbilityKey.Affected);
if (!(o instanceof Player)) { if (!(o instanceof Player)) {
return false; return false;
} }
@@ -59,7 +60,7 @@ public class ReplaceAddCounter extends ReplacementEffect {
if (hasParam("ValidCounterType")) { if (hasParam("ValidCounterType")) {
String type = getParam("ValidCounterType"); String type = getParam("ValidCounterType");
if (CounterType.getType(type) != runParams.get("CounterType")) { if (CounterType.getType(type) != runParams.get(AbilityKey.CounterType)) {
return false; return false;
} }
} }
@@ -71,14 +72,14 @@ public class ReplaceAddCounter extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("CounterNum", runParams.get("CounterNum")); sa.setReplacingObject(AbilityKey.CounterNum, runParams.get(AbilityKey.CounterNum));
sa.setReplacingObject("CounterType", ((CounterType) runParams.get("CounterType")).getName()); sa.setReplacingObject(AbilityKey.CounterType, ((CounterType) runParams.get(AbilityKey.CounterType)).getName());
Object o = runParams.get("Affected"); Object o = runParams.get(AbilityKey.Affected);
if (o instanceof Card) { if (o instanceof Card) {
sa.setReplacingObject("Card", o); sa.setReplacingObject(AbilityKey.Card, o);
} else if (o instanceof Player) { } else if (o instanceof Player) {
sa.setReplacingObject("Player", o); sa.setReplacingObject(AbilityKey.Player, o);
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -42,15 +43,15 @@ public class ReplaceCounter extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
final SpellAbility spellAbility = (SpellAbility) runParams.get("TgtSA"); final SpellAbility spellAbility = (SpellAbility) runParams.get(AbilityKey.TgtSA);
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidCause")) { if (hasParam("ValidCause")) {
if (!matchesValid(runParams.get("Cause"), getParam("ValidCause").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Cause), getParam("ValidCause").split(","), this.getHostCard())) {
return false; return false;
} }
} }
@@ -67,8 +68,8 @@ public class ReplaceCounter extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactoryUtil; import forge.game.card.CardFactoryUtil;
@@ -46,49 +47,49 @@ public class ReplaceDamage extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (!(runParams.containsKey("Prevention") == (hasParam("PreventionEffect") || hasParam("Prevent")))) { if (!(runParams.containsKey(AbilityKey.Prevention) == (hasParam("PreventionEffect") || hasParam("Prevent")))) {
return false; return false;
} }
if (((Integer) runParams.get("DamageAmount")) == 0) { if (((Integer) runParams.get(AbilityKey.DamageAmount)) == 0) {
// If no actual damage is dealt, there is nothing to replace // If no actual damage is dealt, there is nothing to replace
return false; return false;
} }
if (hasParam("ValidSource")) { if (hasParam("ValidSource")) {
String validSource = getParam("ValidSource"); String validSource = getParam("ValidSource");
validSource = AbilityUtils.applyAbilityTextChangeEffects(validSource, this); validSource = AbilityUtils.applyAbilityTextChangeEffects(validSource, this);
if (!matchesValid(runParams.get("DamageSource"), validSource.split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.DamageSource), validSource.split(","), getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidTarget")) { if (hasParam("ValidTarget")) {
String validTarget = getParam("ValidTarget"); String validTarget = getParam("ValidTarget");
validTarget = AbilityUtils.applyAbilityTextChangeEffects(validTarget, this); validTarget = AbilityUtils.applyAbilityTextChangeEffects(validTarget, this);
if (!matchesValid(runParams.get("Affected"), validTarget.split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), validTarget.split(","), getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidCause")) { if (hasParam("ValidCause")) {
if (!runParams.containsKey("Cause")) { if (!runParams.containsKey(AbilityKey.Cause)) {
return false; return false;
} }
SpellAbility cause = (SpellAbility) runParams.get("Cause"); SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
String validCause = getParam("ValidCause"); String validCause = getParam("ValidCause");
validCause = AbilityUtils.applyAbilityTextChangeEffects(validCause, this); validCause = AbilityUtils.applyAbilityTextChangeEffects(validCause, this);
if (!matchesValid(cause, validCause.split(","), getHostCard())) { if (!matchesValid(cause, validCause.split(","), getHostCard())) {
return false; return false;
} }
if (hasParam("CauseIsSource")) { if (hasParam("CauseIsSource")) {
if (!cause.getHostCard().equals(runParams.get("DamageSource"))) { if (!cause.getHostCard().equals(runParams.get(AbilityKey.DamageSource))) {
return false; return false;
} }
} }
} }
if (hasParam("RelativeToSource")) { if (hasParam("RelativeToSource")) {
Card source = (Card) runParams.get("DamageSource"); Card source = (Card) runParams.get(AbilityKey.DamageSource);
String validRelative = getParam("RelativeToSource"); String validRelative = getParam("RelativeToSource");
validRelative = AbilityUtils.applyAbilityTextChangeEffects(validRelative, this); validRelative = AbilityUtils.applyAbilityTextChangeEffects(validRelative, this);
if (!matchesValid(runParams.get("DamageTarget"), validRelative.split(","), source)) { if (!matchesValid(runParams.get(AbilityKey.DamageTarget), validRelative.split(","), source)) {
return false; return false;
} }
} }
@@ -103,17 +104,17 @@ public class ReplaceDamage extends ReplacementEffect {
intoperand = CardFactoryUtil.xCount(getHostCard(), getHostCard().getSVar(operand)); intoperand = CardFactoryUtil.xCount(getHostCard(), getHostCard().getSVar(operand));
} }
if (!Expressions.compare((Integer) runParams.get("DamageAmount"), operator, intoperand)) { if (!Expressions.compare((Integer) runParams.get(AbilityKey.DamageAmount), operator, intoperand)) {
return false; return false;
} }
} }
if (hasParam("IsCombat")) { if (hasParam("IsCombat")) {
if (getParam("IsCombat").equals("True")) { if (getParam("IsCombat").equals("True")) {
if (!((Boolean) runParams.get("IsCombat"))) { if (!((Boolean) runParams.get(AbilityKey.IsCombat))) {
return false; return false;
} }
} else { } else {
if ((Boolean) runParams.get("IsCombat")) { if ((Boolean) runParams.get(AbilityKey.IsCombat)) {
return false; return false;
} }
} }
@@ -149,10 +150,10 @@ public class ReplaceDamage extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("DamageAmount", runParams.get("DamageAmount")); sa.setReplacingObject(AbilityKey.DamageAmount, runParams.get(AbilityKey.DamageAmount));
sa.setReplacingObject("Target", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Target, runParams.get(AbilityKey.Affected));
sa.setReplacingObject("Source", runParams.get("DamageSource")); sa.setReplacingObject(AbilityKey.Source, runParams.get(AbilityKey.DamageSource));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -42,23 +43,23 @@ public class ReplaceDestroy extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
Card card = (Card)runParams.get("Card"); Card card = (Card)runParams.get(AbilityKey.Card);
if (!matchesValid(card, getParam("ValidCard").split(","), getHostCard())) { if (!matchesValid(card, getParam("ValidCard").split(","), getHostCard())) {
return false; return false;
} }
// extra check for Regeneration // extra check for Regeneration
if (hasParam("Regeneration")) { if (hasParam("Regeneration")) {
if (!runParams.containsKey("Regeneration")) { if (!runParams.containsKey(AbilityKey.Regeneration)) {
return false; return false;
} }
if (!(Boolean)runParams.get("Regeneration")) { if (!(Boolean)runParams.get(AbilityKey.Regeneration)) {
return false; return false;
} }
if (!card.canBeShielded()) { if (!card.canBeShielded()) {
@@ -71,7 +72,7 @@ public class ReplaceDestroy extends ReplacementEffect {
} }
} }
if (hasParam("ValidSource")) { if (hasParam("ValidSource")) {
if (!matchesValid(runParams.get("Source"), getParam("ValidSource").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Source), getParam("ValidSource").split(","), getHostCard())) {
return false; return false;
} }
} }
@@ -83,8 +84,8 @@ public class ReplaceDestroy extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Card")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Card));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -42,24 +43,24 @@ public class ReplaceDiscard extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Card"), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Card), getParam("ValidCard").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidSource")) { if (hasParam("ValidSource")) {
if (!matchesValid(runParams.get("Source"), getParam("ValidSource").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Source), getParam("ValidSource").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("DiscardFromEffect")) { if (hasParam("DiscardFromEffect")) {
if (null == runParams.get("Source")) { if (null == runParams.get(AbilityKey.Source)) {
return false; return false;
} }
} }
@@ -71,10 +72,10 @@ public class ReplaceDiscard extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Card")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Card));
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
sa.setReplacingObject("Source", runParams.get("Source")); sa.setReplacingObject(AbilityKey.Source, runParams.get(AbilityKey.Source));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
@@ -44,14 +45,14 @@ public class ReplaceDraw extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("NotFirstCardInDrawStep")) { if (hasParam("NotFirstCardInDrawStep")) {
final Player p = (Player)runParams.get("Affected"); final Player p = (Player)runParams.get(AbilityKey.Affected);
if (p.numDrawnThisDrawStep() == 0 if (p.numDrawnThisDrawStep() == 0
&& this.getHostCard().getGame().getPhaseHandler().is(PhaseType.DRAW) && this.getHostCard().getGame().getPhaseHandler().is(PhaseType.DRAW)
&& this.getHostCard().getGame().getPhaseHandler().isPlayerTurn(p)) { && this.getHostCard().getGame().getPhaseHandler().isPlayerTurn(p)) {
@@ -68,7 +69,7 @@ public class ReplaceDraw extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.util.Expressions; import forge.util.Expressions;
@@ -43,14 +44,14 @@ public class ReplaceDrawCards extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("Number")) { if (hasParam("Number")) {
final int n = (Integer)runParams.get("Number"); final int n = (Integer)runParams.get(AbilityKey.Number);
String comparator = getParam("Number"); String comparator = getParam("Number");
final String operator = comparator.substring(0, 2); final String operator = comparator.substring(0, 2);
final int operandValue = Integer.parseInt(comparator.substring(2)); final int operandValue = Integer.parseInt(comparator.substring(2));
@@ -68,7 +69,7 @@ public class ReplaceDrawCards extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -42,22 +43,22 @@ public class ReplaceGainLife extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (((int)runParams.get("LifeGained")) <= 0) { if (((int)runParams.get(AbilityKey.LifeGained)) <= 0) {
return false; return false;
} }
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidSource")) { if (hasParam("ValidSource")) {
if (!matchesValid(runParams.get("Source"), getParam("ValidSource").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Source), getParam("ValidSource").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if ("True".equals(getParam("SourceController"))) { if ("True".equals(getParam("SourceController"))) {
if (runParams.get("Source") == null || !runParams.get("Affected").equals(((Card)runParams.get("Source")).getController())) { if (runParams.get(AbilityKey.Source) == null || !runParams.get(AbilityKey.Affected).equals(((Card)runParams.get(AbilityKey.Source)).getController())) {
return false; return false;
} }
} }
@@ -69,9 +70,9 @@ public class ReplaceGainLife extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("LifeGained", runParams.get("LifeGained")); sa.setReplacingObject(AbilityKey.LifeGained, runParams.get(AbilityKey.LifeGained));
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import java.util.Map; import java.util.Map;
@@ -24,9 +25,9 @@ public class ReplaceGameLoss extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player; import forge.game.player.Player;
@@ -28,9 +29,9 @@ public class ReplaceMoved extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
final Player controller = getHostCard().getController(); final Player controller = getHostCard().getController();
final Card affected = (Card) runParams.get("Affected"); final Card affected = (Card) runParams.get(AbilityKey.Affected);
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(affected, getParam("ValidCard").split(","), getHostCard())) { if (!matchesValid(affected, getParam("ValidCard").split(","), getHostCard())) {
@@ -39,27 +40,27 @@ public class ReplaceMoved extends ReplacementEffect {
} }
if (hasParam("ValidLKI")) { if (hasParam("ValidLKI")) {
if (!matchesValid(runParams.get("CardLKI"), getParam("ValidLKI").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.CardLKI), getParam("ValidLKI").split(","), getHostCard())) {
return false; return false;
} }
} }
if (hasParam("Origin")) { if (hasParam("Origin")) {
ZoneType zt = (ZoneType) runParams.get("Origin"); ZoneType zt = (ZoneType) runParams.get(AbilityKey.Origin);
if (!ZoneType.listValueOf(getParam("Origin")).contains(zt)) { if (!ZoneType.listValueOf(getParam("Origin")).contains(zt)) {
return false; return false;
} }
} }
if (hasParam("Destination")) { if (hasParam("Destination")) {
ZoneType zt = (ZoneType) runParams.get("Destination"); ZoneType zt = (ZoneType) runParams.get(AbilityKey.Destination);
if (!ZoneType.listValueOf(getParam("Destination")).contains(zt)) { if (!ZoneType.listValueOf(getParam("Destination")).contains(zt)) {
return false; return false;
} }
} }
if (hasParam("ExcludeDestination")) { if (hasParam("ExcludeDestination")) {
ZoneType zt = (ZoneType) runParams.get("Destination"); ZoneType zt = (ZoneType) runParams.get(AbilityKey.Destination);
if (ZoneType.listValueOf(getParam("ExcludeDestination")).contains(zt)) { if (ZoneType.listValueOf(getParam("ExcludeDestination")).contains(zt)) {
return false; return false;
} }
@@ -67,29 +68,29 @@ public class ReplaceMoved extends ReplacementEffect {
if (hasParam("Fizzle")) { if (hasParam("Fizzle")) {
// if Replacement look for Fizzle // if Replacement look for Fizzle
if (!runParams.containsKey("Fizzle")) { if (!runParams.containsKey(AbilityKey.Fizzle)) {
return false; return false;
} }
Boolean val = (Boolean) runParams.get("Fizzle"); Boolean val = (Boolean) runParams.get(AbilityKey.Fizzle);
if ("True".equals(getParam("Fizzle")) != val) { if ("True".equals(getParam("Fizzle")) != val) {
return false; return false;
} }
} }
if (hasParam("ValidStackSa")) { if (hasParam("ValidStackSa")) {
if (!runParams.containsKey("StackSa")) { if (!runParams.containsKey(AbilityKey.StackSa)) {
return false; return false;
} }
if (!((SpellAbility)runParams.get("StackSa")).isValid(getParam("ValidStackSa").split(","), getHostCard().getController(), getHostCard(), null)) { if (!((SpellAbility)runParams.get(AbilityKey.StackSa)).isValid(getParam("ValidStackSa").split(","), getHostCard().getController(), getHostCard(), null)) {
return false; return false;
} }
} }
if (hasParam("Cause")) { if (hasParam("Cause")) {
if (!runParams.containsKey("Cause")) { if (!runParams.containsKey(AbilityKey.Cause)) {
return false; return false;
} }
SpellAbility cause = (SpellAbility) runParams.get("Cause"); SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
if (cause == null) { if (cause == null) {
return false; return false;
} }
@@ -99,8 +100,8 @@ public class ReplaceMoved extends ReplacementEffect {
} }
if (hasParam("NotCause")) { if (hasParam("NotCause")) {
if (runParams.containsKey("Cause")) { if (runParams.containsKey(AbilityKey.Cause)) {
SpellAbility cause = (SpellAbility) runParams.get("Cause"); SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
if (cause != null) { if (cause != null) {
if (cause.isValid(getParam("NotCause").split(","), controller, getHostCard(), null)) { if (cause.isValid(getParam("NotCause").split(","), controller, getHostCard(), null)) {
return false; return false;
@@ -116,10 +117,10 @@ public class ReplaceMoved extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
sa.setReplacingObject("CardLKI", runParams.get("CardLKI")); sa.setReplacingObject(AbilityKey.CardLKI, runParams.get(AbilityKey.CardLKI));
sa.setReplacingObject("Cause", runParams.get("Cause")); sa.setReplacingObject(AbilityKey.Cause, runParams.get(AbilityKey.Cause));
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactoryUtil; import forge.game.card.CardFactoryUtil;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -29,10 +30,10 @@ public class ReplaceProduceMana extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
//Check for tapping //Check for tapping
if (!hasParam("NoTapCheck")) { if (!hasParam("NoTapCheck")) {
final SpellAbility manaAbility = (SpellAbility) runParams.get("AbilityMana"); final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
if (manaAbility == null || manaAbility.getRootAbility().getPayCosts() == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { if (manaAbility == null || manaAbility.getRootAbility().getPayCosts() == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) {
return false; return false;
} }
@@ -48,14 +49,14 @@ public class ReplaceProduceMana extends ReplacementEffect {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
intoperand = CardFactoryUtil.xCount(getHostCard(), getHostCard().getSVar(operand)); intoperand = CardFactoryUtil.xCount(getHostCard(), getHostCard().getSVar(operand));
} }
int manaAmount = StringUtils.countMatches((String) runParams.get("Mana"), " ") + 1; int manaAmount = StringUtils.countMatches((String) runParams.get(AbilityKey.Mana), " ") + 1;
if (!Expressions.compare(manaAmount, operator, intoperand)) { if (!Expressions.compare(manaAmount, operator, intoperand)) {
return false; return false;
} }
} }
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
return false; return false;
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import java.util.Map; import java.util.Map;
@@ -41,9 +42,9 @@ public class ReplaceSetInMotion extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), getHostCard())) {
return false; return false;
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -25,13 +26,13 @@ public class ReplaceSurveil extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.Map) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.Map)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (((int) runParams.get("SurveilNum")) <= 0) { if (((int) runParams.get(AbilityKey.SurveilNum)) <= 0) {
return false; return false;
} }
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), this.getHostCard())) {
return false; return false;
} }
} }
@@ -43,9 +44,9 @@ public class ReplaceSurveil extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
sa.setReplacingObject("SurveilNum", runParams.get("SurveilNum")); sa.setReplacingObject(AbilityKey.SurveilNum, runParams.get(AbilityKey.SurveilNum));
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -25,27 +26,27 @@ public class ReplaceToken extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.Map) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.Map)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (((int) runParams.get("TokenNum")) <= 0) { if (((int) runParams.get(AbilityKey.TokenNum)) <= 0) {
return false; return false;
} }
if (hasParam("EffectOnly")) { if (hasParam("EffectOnly")) {
final Boolean effectOnly = (Boolean) runParams.get("EffectOnly"); final Boolean effectOnly = (Boolean) runParams.get(AbilityKey.EffectOnly);
if (!effectOnly) { if (!effectOnly) {
return false; return false;
} }
} }
if (hasParam("ValidPlayer")) { if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidPlayer").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidPlayer").split(","), getHostCard())) {
return false; return false;
} }
} }
if (hasParam("ValidToken")) { if (hasParam("ValidToken")) {
if (runParams.containsKey("Token")) { if (runParams.containsKey(AbilityKey.Token)) {
if (!matchesValid(runParams.get("Token"), getParam("ValidToken").split(","), getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Token), getParam("ValidToken").split(","), getHostCard())) {
return false; return false;
} }
} else { } else {
@@ -61,9 +62,9 @@ public class ReplaceToken extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.Map, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("TokenNum", runParams.get("TokenNum")); sa.setReplacingObject(AbilityKey.TokenNum, runParams.get(AbilityKey.TokenNum));
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -25,9 +26,9 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
return false; return false;
} }
} }
@@ -38,8 +39,8 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
@@ -44,14 +45,14 @@ public class ReplaceUntap extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(Map<String, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get("Affected"), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
return false; return false;
} }
} }
if (hasParam("UntapStep")) { if (hasParam("UntapStep")) {
final Object o = runParams.get("Affected"); final Object o = runParams.get(AbilityKey.Affected);
//normally should not happen, but protect from possible crash. //normally should not happen, but protect from possible crash.
if (!(o instanceof Card)) { if (!(o instanceof Card)) {
return false; return false;
@@ -72,8 +73,8 @@ public class ReplaceUntap extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
} }
} }

View File

@@ -19,6 +19,7 @@ package forge.game.replacement;
import forge.game.Game; import forge.game.Game;
import forge.game.TriggerReplacementBase; import forge.game.TriggerReplacementBase;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
@@ -121,7 +122,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* the run params * the run params
* @return true, if successful * @return true, if successful
*/ */
public abstract boolean canReplace(final Map<String, Object> runParams); public abstract boolean canReplace (final Map<AbilityKey, Object> runParams);
/** /**
* <p> * <p>
@@ -193,19 +194,16 @@ 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;
} }
/** /**
* Sets the replacing objects. * Sets the replacing objects.
* * @param runParams
* @param runParams
* the run params * the run params
* @param spellAbility * @param spellAbility
* the SpellAbility
*/ */
public void setReplacingObjects(final Map<String, Object> runParams, final SpellAbility spellAbility) { public void setReplacingObjects(final Map<AbilityKey, Object> runParams, final SpellAbility spellAbility) {
// Should be overridden by replacers that need it. // Should be overridden by replacers that need it.
} }

View File

@@ -21,6 +21,7 @@ import forge.card.MagicColor;
import forge.game.Game; import forge.game.Game;
import forge.game.GameLogEntryType; import forge.game.GameLogEntryType;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
@@ -52,41 +53,17 @@ public class ReplacementHandler {
//private final List<ReplacementEffect> tmpEffects = new ArrayList<ReplacementEffect>(); //private final List<ReplacementEffect> tmpEffects = new ArrayList<ReplacementEffect>();
public ReplacementResult run(ReplacementType event, final Map<String, Object> runParams) { public List<ReplacementEffect> getReplacementList(final ReplacementType event, final Map<AbilityKey, Object> runParams, final ReplacementLayer layer) {
final Object affected = runParams.get("Affected");
Player decider = null;
// Figure out who decides which of multiple replacements to apply
// as well as whether or not to apply optional replacements.
if (affected instanceof Player) {
decider = (Player) affected;
} else {
decider = ((Card) affected).getController();
}
// try out all layer
for (ReplacementLayer layer : ReplacementLayer.values()) {
ReplacementResult res = run(event, runParams, layer, decider);
if (res != ReplacementResult.NotReplaced) {
return res;
}
}
return ReplacementResult.NotReplaced;
}
public List<ReplacementEffect> getReplacementList(final ReplacementType event, final Map<String, Object> runParams, final ReplacementLayer layer) {
final CardCollection preList = new CardCollection(); final CardCollection preList = new CardCollection();
boolean checkAgain = false; boolean checkAgain = false;
Card affectedLKI = null; Card affectedLKI = null;
Card affectedCard = null; Card affectedCard = null;
if (ReplacementType.Moved.equals(event) && ZoneType.Battlefield.equals(runParams.get("Destination"))) { if (ReplacementType.Moved.equals(event) && ZoneType.Battlefield.equals(runParams.get(AbilityKey.Destination))) {
// if it was caused by an replacement effect, use the already calculated RE list // if it was caused by an replacement effect, use the already calculated RE list
// otherwise the RIOT card would cause a StackError // otherwise the RIOT card would cause a StackError
SpellAbility cause = (SpellAbility) runParams.get("Cause"); SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
if (cause != null && cause.isReplacementAbility()) { if (cause != null && cause.isReplacementAbility()) {
final ReplacementEffect re = cause.getReplacementEffect(); final ReplacementEffect re = cause.getReplacementEffect();
// only return for same layer // only return for same layer
@@ -96,13 +73,13 @@ public class ReplacementHandler {
} }
// Rule 614.12 Enter the Battlefield Replacement Effects look at what the card would be on the battlefield // Rule 614.12 Enter the Battlefield Replacement Effects look at what the card would be on the battlefield
affectedCard = (Card) runParams.get("Affected"); affectedCard = (Card) runParams.get(AbilityKey.Affected);
affectedLKI = CardUtil.getLKICopy(affectedCard); affectedLKI = CardUtil.getLKICopy(affectedCard);
affectedLKI.setLastKnownZone(affectedCard.getController().getZone(ZoneType.Battlefield)); affectedLKI.setLastKnownZone(affectedCard.getController().getZone(ZoneType.Battlefield));
preList.add(affectedLKI); preList.add(affectedLKI);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList); game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList);
checkAgain = true; checkAgain = true;
runParams.put("Affected", affectedLKI); runParams.put(AbilityKey.Affected, affectedLKI);
} }
final List<ReplacementEffect> possibleReplacers = Lists.newArrayList(); final List<ReplacementEffect> possibleReplacers = Lists.newArrayList();
@@ -157,7 +134,7 @@ public class ReplacementHandler {
for (final ReplacementEffect re : affectedLKI.getReplacementEffects()) { for (final ReplacementEffect re : affectedLKI.getReplacementEffects()) {
re.setHostCard(affectedCard); re.setHostCard(affectedCard);
} }
runParams.put("Affected", affectedCard); runParams.put(AbilityKey.Affected, affectedCard);
} }
game.getAction().checkStaticAbilities(false); game.getAction().checkStaticAbilities(false);
} }
@@ -171,16 +148,40 @@ public class ReplacementHandler {
* *
* @param runParams * @param runParams
* the run params,same as for triggers. * the run params,same as for triggers.
* @return true if the event was replaced. * @return ReplacementResult, an enum that represents what happened to the replacement effect.
*/ */
public ReplacementResult run(final ReplacementType event, final Map<String, Object> runParams, final ReplacementLayer layer, final Player decider) { public ReplacementResult run(ReplacementType event, final Map<AbilityKey, Object> runParams) {
final Object affected = runParams.get(AbilityKey.Affected);
Player decider = null;
// Figure out who decides which of multiple replacements to apply
// as well as whether or not to apply optional replacements.
if (affected instanceof Player) {
decider = (Player) affected;
} else {
decider = ((Card) affected).getController();
}
// try out all layer
for (ReplacementLayer layer : ReplacementLayer.values()) {
ReplacementResult res = run(event, runParams, layer, decider);
if (res != ReplacementResult.NotReplaced) {
return res;
}
}
return ReplacementResult.NotReplaced;
}
private ReplacementResult run(final ReplacementType event, final Map<AbilityKey, Object> runParams, final ReplacementLayer layer, final Player decider) {
final List<ReplacementEffect> possibleReplacers = getReplacementList(event, runParams, layer); final List<ReplacementEffect> possibleReplacers = getReplacementList(event, runParams, layer);
if (possibleReplacers.isEmpty()) { if (possibleReplacers.isEmpty()) {
return ReplacementResult.NotReplaced; return ReplacementResult.NotReplaced;
} }
ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect("Choose a replacement effect to apply first.", possibleReplacers, runParams); ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect("Choose a replacement effect to apply first.", possibleReplacers);
possibleReplacers.remove(chosenRE); possibleReplacers.remove(chosenRE);
@@ -213,7 +214,7 @@ public class ReplacementHandler {
* @param replacementEffect * @param replacementEffect
* the replacement effect to run * the replacement effect to run
*/ */
private ReplacementResult executeReplacement(final Map<String, Object> runParams, private ReplacementResult executeReplacement(final Map<AbilityKey, Object> runParams,
final ReplacementEffect replacementEffect, final Player decider, final Game game) { final ReplacementEffect replacementEffect, final Player decider, final Game game) {
final Map<String, String> mapParams = replacementEffect.getMapParams(); final Map<String, String> mapParams = replacementEffect.getMapParams();
@@ -237,7 +238,7 @@ public class ReplacementHandler {
do { do {
replacementEffect.setReplacingObjects(runParams, tailend); replacementEffect.setReplacingObjects(runParams, tailend);
//set original Params to update them later //set original Params to update them later
tailend.setReplacingObject("OriginalParams", runParams); tailend.setReplacingObject(AbilityKey.OriginalParams, runParams);
tailend = tailend.getSubAbility(); tailend = tailend.getSubAbility();
} while(tailend != null); } while(tailend != null);
@@ -248,7 +249,7 @@ public class ReplacementHandler {
do { do {
replacementEffect.setReplacingObjects(runParams, tailend); replacementEffect.setReplacingObjects(runParams, tailend);
//set original Params to update them later //set original Params to update them later
tailend.setReplacingObject("OriginalParams", runParams); tailend.setReplacingObject(AbilityKey.OriginalParams, runParams);
tailend = tailend.getSubAbility(); tailend = tailend.getSubAbility();
} while(tailend != null); } while(tailend != null);
} }
@@ -275,7 +276,7 @@ public class ReplacementHandler {
Card cardForUi = host.getCardForUi(); Card cardForUi = host.getCardForUi();
String effectDesc = TextUtil.fastReplace(replacementEffect.toString(), "CARDNAME", cardForUi.getName()); String effectDesc = TextUtil.fastReplace(replacementEffect.toString(), "CARDNAME", cardForUi.getName());
final String question = replacementEffect instanceof ReplaceDiscard final String question = replacementEffect instanceof ReplaceDiscard
? TextUtil.concatWithSpace("Apply replacement effect of", cardForUi.toString(), "to", TextUtil.addSuffix(runParams.get("Card").toString(),"?\r\n"), TextUtil.enclosedParen(effectDesc)) ? TextUtil.concatWithSpace("Apply replacement effect of", cardForUi.toString(), "to", TextUtil.addSuffix(runParams.get(AbilityKey.Card).toString(),"?\r\n"), TextUtil.enclosedParen(effectDesc))
: TextUtil.concatWithSpace("Apply replacement effect of", TextUtil.addSuffix(cardForUi.toString(),"?\r\n"), TextUtil.enclosedParen(effectDesc)); : TextUtil.concatWithSpace("Apply replacement effect of", TextUtil.addSuffix(cardForUi.toString(),"?\r\n"), TextUtil.enclosedParen(effectDesc));
boolean confirmed = optDecider.getController().confirmReplacementEffect(replacementEffect, effectSA, question); boolean confirmed = optDecider.getController().confirmReplacementEffect(replacementEffect, effectSA, question);
if (!confirmed) { if (!confirmed) {
@@ -292,9 +293,9 @@ public class ReplacementHandler {
Player player = host.getController(); Player player = host.getController();
if (mapParams.containsKey("ManaReplacement")) { if (mapParams.containsKey("ManaReplacement")) {
final SpellAbility manaAb = (SpellAbility) runParams.get("AbilityMana"); final SpellAbility manaAb = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
final Player player1 = (Player) runParams.get("Player"); final Player player1 = (Player) runParams.get(AbilityKey.Player);
final String rep = (String) runParams.get("Mana"); final String rep = (String) runParams.get(AbilityKey.Mana);
// Replaced mana type // Replaced mana type
final Card repHost = host; final Card repHost = host;
String repType = repHost.getSVar(mapParams.get("ManaReplacement")); String repType = repHost.getSVar(mapParams.get("ManaReplacement"));
@@ -307,8 +308,8 @@ public class ReplacementHandler {
player.getController().playSpellAbilityNoStack(effectSA, true); player.getController().playSpellAbilityNoStack(effectSA, true);
// if the spellability is a replace effect then its some new logic // if the spellability is a replace effect then its some new logic
// if ReplacementResult is set in run params use that instead // if ReplacementResult is set in run params use that instead
if (runParams.containsKey("ReplacementResult")) { if (runParams.containsKey(AbilityKey.ReplacementResult)) {
return (ReplacementResult) runParams.get("ReplacementResult"); return (ReplacementResult) runParams.get(AbilityKey.ReplacementResult);
} }
} }
@@ -356,31 +357,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

@@ -128,11 +128,10 @@ public class AbilityManaPart implements java.io.Serializable {
final Card source = this.getSourceCard(); final Card source = this.getSourceCard();
final ManaPool manaPool = player.getManaPool(); final ManaPool manaPool = player.getManaPool();
String afterReplace = applyManaReplacement(sa, produced); String afterReplace = applyManaReplacement(sa, produced);
final Map<String, Object> repParams = Maps.newHashMap(); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source);
repParams.put("Mana", afterReplace); repParams.put(AbilityKey.Mana, afterReplace);
repParams.put("Affected", source); repParams.put(AbilityKey.Player, player);
repParams.put("Player", player); repParams.put(AbilityKey.AbilityMana, sa);
repParams.put("AbilityMana", sa);
if (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams) != ReplacementResult.NotReplaced) { if (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams) != ReplacementResult.NotReplaced) {
return; return;
} }

View File

@@ -143,7 +143,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private EnumMap<AbilityKey, Object> triggeringObjects = AbilityKey.newMap(); private EnumMap<AbilityKey, Object> triggeringObjects = AbilityKey.newMap();
private HashMap<String, Object> replacingObjects = Maps.newHashMap(); private EnumMap<AbilityKey, Object> replacingObjects = AbilityKey.newMap();
private List<AbilitySub> chosenList = null; private List<AbilitySub> chosenList = null;
private CardCollection tappedForConvoke = new CardCollection(); private CardCollection tappedForConvoke = new CardCollection();
@@ -593,17 +593,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
triggerRemembered = Lists.newArrayList(); triggerRemembered = Lists.newArrayList();
} }
public HashMap<String, Object> getReplacingObjects() { public Map<AbilityKey, Object> getReplacingObjects() {
return replacingObjects; return replacingObjects;
} }
public boolean hasReplacingObject(final String type) { public Object getReplacingObject(final AbilityKey type) {
return replacingObjects.containsKey(type);
}
public Object getReplacingObject(final String type) {
final Object res = replacingObjects.get(type); final Object res = replacingObjects.get(type);
return res; return res;
} }
public void setReplacingObject(final String type, final Object o) {
public void setReplacingObject(final AbilityKey type, final Object o) {
replacingObjects.put(type, o); replacingObjects.put(type, o);
} }

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);
}
} }
} }

View File

@@ -506,7 +506,7 @@ public class PlayerControllerForTests extends PlayerController {
} }
@Override @Override
public ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, Map<String, Object> runParams) { public ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return Iterables.getFirst(possibleReplacers, null); return Iterables.getFirst(possibleReplacers, null);
} }

View File

@@ -19,6 +19,7 @@ Myrd
nefigah nefigah
OgreBattlecruiser OgreBattlecruiser
pfps pfps
Ryan1729
Seravy Seravy
Sirspud Sirspud
Sloth Sloth

View File

@@ -0,0 +1,17 @@
[metadata]
Name:Possibility Storm - Throne of Eldraine #00 (Pre-release Puzzle)
URL:http://www.possibilitystorm.com/wp-content/uploads/2019/10/130.-ELD001.jpg
Goal:Win
Turns:1
Difficulty:Uncommon
Description:Win this turn.
[state]
humanlife=20
ailife=12
turn=1
activeplayer=human
activephase=MAIN1
humanhand=Giant Opportunity;Mirrormade;Curious Pair;Frogify
humanbattlefield=Witch's Oven;Wicked Wolf;Maraleaf Rider;Oko, Thief of Crowns|Counters:LOYALTY=6;Forest;Forest;Forest;Island;Island;Island
aibattlefield=Wojek Bodyguard;Loyal Pegasus
humanprecast=Oko, Thief of Crowns:1

View File

@@ -1,13 +1,10 @@
package forge.match.input; package forge.match.input;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import forge.GuiBase; import forge.GuiBase;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -363,11 +360,11 @@ public abstract class InputPayMana extends InputSyncronizedBase {
final Card source = am.getHostCard(); final Card source = am.getHostCard();
final Player activator = am.getActivatingPlayer(); final Player activator = am.getActivatingPlayer();
final Game g = source.getGame(); final Game g = source.getGame();
final HashMap<String, Object> repParams = new HashMap<>(); final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put("Mana", m.getOrigProduced()); repParams.put(AbilityKey.Mana, m.getOrigProduced());
repParams.put("Affected", source); repParams.put(AbilityKey.Affected, source);
repParams.put("Player", activator); repParams.put(AbilityKey.Player, activator);
repParams.put("AbilityMana", am); repParams.put(AbilityKey.AbilityMana, am);
for (final Player p : g.getPlayers()) { for (final Player p : g.getPlayers()) {
for (final Card crd : p.getAllCards()) { for (final Card crd : p.getAllCards()) {

View File

@@ -1587,7 +1587,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
@Override @Override
public ReplacementEffect chooseSingleReplacementEffect(final String prompt, public ReplacementEffect chooseSingleReplacementEffect(final String prompt,
final List<ReplacementEffect> possibleReplacers, final Map<String, Object> runParams) { final List<ReplacementEffect> possibleReplacers) {
final ReplacementEffect first = possibleReplacers.get(0); final ReplacementEffect first = possibleReplacers.get(0);
if (possibleReplacers.size() == 1) { if (possibleReplacers.size() == 1) {
return first; return first;