mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
CardFactoryUtil: make Haunt a better Effect, rework the Haunt Triggers in new format
Trigger now does has a helper to replace ABILITY in TriggerDescription with its ability
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -402,6 +402,7 @@ forge-game/src/main/java/forge/game/ability/effects/FogEffect.java -text
|
|||||||
forge-game/src/main/java/forge/game/ability/effects/GameLossEffect.java -text
|
forge-game/src/main/java/forge/game/ability/effects/GameLossEffect.java -text
|
||||||
forge-game/src/main/java/forge/game/ability/effects/GameWinEffect.java -text
|
forge-game/src/main/java/forge/game/ability/effects/GameWinEffect.java -text
|
||||||
forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java -text svneol=unset#text/plain
|
forge-game/src/main/java/forge/game/ability/effects/GoadEffect.java -text svneol=unset#text/plain
|
||||||
|
forge-game/src/main/java/forge/game/ability/effects/HauntEffect.java -text svneol=unset#text/plain
|
||||||
forge-game/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text
|
forge-game/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text
|
||||||
forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text
|
forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text
|
||||||
forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text
|
forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
@@ -13,7 +12,7 @@ import forge.util.ReflectionUtil;
|
|||||||
public enum SpellApiToAi {
|
public enum SpellApiToAi {
|
||||||
Converter;
|
Converter;
|
||||||
|
|
||||||
private final Map<ApiType, SpellAbilityAi> apiToInstance = new EnumMap<>(ApiType.class);
|
private final Map<ApiType, SpellAbilityAi> apiToInstance = Maps.newEnumMap(ApiType.class);
|
||||||
|
|
||||||
// Do the extra copy to make an actual EnumMap (faster)
|
// Do the extra copy to make an actual EnumMap (faster)
|
||||||
private final Map<ApiType, Class<? extends SpellAbilityAi>> apiToClass = Maps.newEnumMap(ImmutableMap
|
private final Map<ApiType, Class<? extends SpellAbilityAi>> apiToClass = Maps.newEnumMap(ImmutableMap
|
||||||
@@ -79,6 +78,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.GainOwnership, CannotPlayAi.class)
|
.put(ApiType.GainOwnership, CannotPlayAi.class)
|
||||||
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
|
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
|
||||||
.put(ApiType.Goad, GoadAi.class)
|
.put(ApiType.Goad, GoadAi.class)
|
||||||
|
.put(ApiType.Haunt, HauntAi.class)
|
||||||
.put(ApiType.LoseLife, LifeLoseAi.class)
|
.put(ApiType.LoseLife, LifeLoseAi.class)
|
||||||
.put(ApiType.LosesGame, GameLossAi.class)
|
.put(ApiType.LosesGame, GameLossAi.class)
|
||||||
.put(ApiType.Mana, ManaEffectAi.class)
|
.put(ApiType.Mana, ManaEffectAi.class)
|
||||||
@@ -149,7 +149,6 @@ public enum SpellApiToAi {
|
|||||||
|
|
||||||
.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class)
|
.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class)
|
||||||
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
||||||
.put(ApiType.InternalHaunt, HauntAi.class)
|
|
||||||
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,35 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.game.card.Card;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.player.Player;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import forge.ai.ComputerUtilCard;
|
||||||
|
import forge.ai.SpellAbilityAi;
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.card.CardPredicates;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class HauntAi extends SpellAbilityAi {
|
public class HauntAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return false; // should not get here
|
final Card card = sa.getHostCard();
|
||||||
}
|
final Game game = ai.getGame();
|
||||||
|
if (sa.usesTargeting() && !card.isToken()) {
|
||||||
|
final List<Card> creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield),
|
||||||
|
CardPredicates.Presets.CREATURES);
|
||||||
|
|
||||||
@Override
|
// nothing to haunt
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> creats, boolean isOptional, Player targetedPlayer) {
|
if (creats.isEmpty()) {
|
||||||
final List<Card> oppCreats = CardLists.filterControlledBy(creats, ai.getOpponents());
|
return false;
|
||||||
return ComputerUtilCard.getWorstCreatureAI(oppCreats.isEmpty() ? creats : oppCreats);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
final List<Card> oppCreats = CardLists.filterControlledBy(creats, ai.getOpponents());
|
||||||
|
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(oppCreats.isEmpty() ? creats : oppCreats));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -25,10 +25,11 @@ import forge.game.spellability.*;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.FileSection;
|
import forge.util.FileSection;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* AbilityFactory class.
|
* AbilityFactory class.
|
||||||
@@ -199,14 +200,14 @@ public final class AbilityFactory {
|
|||||||
|
|
||||||
if (type != AbilityRecordType.SubAbility) { // SubAbilities don't have Costs or Cost
|
if (type != AbilityRecordType.SubAbility) { // SubAbilities don't have Costs or Cost
|
||||||
// descriptors
|
// descriptors
|
||||||
sb.append(spellAbility.getCostDescription());
|
sb.append(spellAbility.getCostDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(mapParams.get("SpellDescription"));
|
sb.append(mapParams.get("SpellDescription"));
|
||||||
|
|
||||||
spellAbility.setDescription(sb.toString());
|
spellAbility.setDescription(sb.toString());
|
||||||
} else if (api == ApiType.Charm) {
|
} else if (api == ApiType.Charm) {
|
||||||
spellAbility.setDescription(CharmEffect.makeSpellDescription(spellAbility));
|
spellAbility.setDescription(CharmEffect.makeSpellDescription(spellAbility));
|
||||||
} else {
|
} else {
|
||||||
spellAbility.setDescription("");
|
spellAbility.setDescription("");
|
||||||
}
|
}
|
||||||
@@ -354,7 +355,7 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final void adjustChangeZoneTarget(final Map<String, String> params, final SpellAbility sa) {
|
public static final void adjustChangeZoneTarget(final Map<String, String> params, final SpellAbility sa) {
|
||||||
List<ZoneType> origin = new ArrayList<ZoneType>();
|
List<ZoneType> origin = Lists.newArrayList();
|
||||||
if (params.containsKey("Origin")) {
|
if (params.containsKey("Origin")) {
|
||||||
origin = ZoneType.listValueOf(params.get("Origin"));
|
origin = ZoneType.listValueOf(params.get("Origin"));
|
||||||
}
|
}
|
||||||
@@ -396,12 +397,12 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final SpellAbility buildEntwineAbility(final SpellAbility sa) {
|
public static final SpellAbility buildEntwineAbility(final SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String[] saChoices = sa.getParam("Choices").split(",");
|
final String[] saChoices = sa.getParam("Choices").split(",");
|
||||||
if (sa.getApi() != ApiType.Charm || saChoices.length != 2)
|
if (sa.getApi() != ApiType.Charm || saChoices.length != 2)
|
||||||
throw new IllegalStateException("Entwine ability may be built only on charm cards");
|
throw new IllegalStateException("Entwine ability may be built only on charm cards");
|
||||||
final String ab = source.getSVar(saChoices[0]);
|
final String ab = source.getSVar(saChoices[0]);
|
||||||
Map<String, String> firstMap = getMapParams(ab);
|
Map<String, String> firstMap = getMapParams(ab);
|
||||||
AbilityRecordType firstType = AbilityRecordType.getRecordType(firstMap);
|
AbilityRecordType firstType = AbilityRecordType.getRecordType(firstMap);
|
||||||
ApiType firstApi = firstType.getApiTypeOf(firstMap);
|
ApiType firstApi = firstType.getApiTypeOf(firstMap);
|
||||||
firstMap.put("StackDecription", firstMap.get("SpellDescription"));
|
firstMap.put("StackDecription", firstMap.get("SpellDescription"));
|
||||||
@@ -409,7 +410,7 @@ public final class AbilityFactory {
|
|||||||
SpellAbility entwineSA = getAbility(AbilityRecordType.Spell, firstApi, firstMap, new Cost(sa.getPayCosts().toSimpleString(), false), source);
|
SpellAbility entwineSA = getAbility(AbilityRecordType.Spell, firstApi, firstMap, new Cost(sa.getPayCosts().toSimpleString(), false), source);
|
||||||
|
|
||||||
final String ab2 = source.getSVar(saChoices[1]);
|
final String ab2 = source.getSVar(saChoices[1]);
|
||||||
Map<String, String> secondMap = getMapParams(ab2);
|
Map<String, String> secondMap = getMapParams(ab2);
|
||||||
ApiType secondApi = firstType.getApiTypeOf(secondMap);
|
ApiType secondApi = firstType.getApiTypeOf(secondMap);
|
||||||
secondMap.put("StackDecription", secondMap.get("SpellDescription"));
|
secondMap.put("StackDecription", secondMap.get("SpellDescription"));
|
||||||
secondMap.put("SpellDescription", "");
|
secondMap.put("SpellDescription", "");
|
||||||
@@ -419,7 +420,7 @@ public final class AbilityFactory {
|
|||||||
entwineSA.setBasicSpell(false);
|
entwineSA.setBasicSpell(false);
|
||||||
entwineSA.setActivatingPlayer(sa.getActivatingPlayer());
|
entwineSA.setActivatingPlayer(sa.getActivatingPlayer());
|
||||||
entwineSA.setRestrictions(sa.getRestrictions());
|
entwineSA.setRestrictions(sa.getRestrictions());
|
||||||
return entwineSA;
|
return entwineSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end class AbilityFactory
|
} // end class AbilityFactory
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import forge.game.ability.effects.*;
|
|||||||
import forge.util.ReflectionUtil;
|
import forge.util.ReflectionUtil;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
@@ -69,11 +70,12 @@ public enum ApiType {
|
|||||||
Fight (FightEffect.class),
|
Fight (FightEffect.class),
|
||||||
FlipACoin (FlipCoinEffect.class),
|
FlipACoin (FlipCoinEffect.class),
|
||||||
Fog (FogEffect.class),
|
Fog (FogEffect.class),
|
||||||
Goad (GoadEffect.class),
|
|
||||||
GainControl (ControlGainEffect.class),
|
GainControl (ControlGainEffect.class),
|
||||||
GainLife (LifeGainEffect.class),
|
GainLife (LifeGainEffect.class),
|
||||||
GainOwnership (OwnershipGainEffect.class),
|
GainOwnership (OwnershipGainEffect.class),
|
||||||
GenericChoice (ChooseGenericEffect.class),
|
GenericChoice (ChooseGenericEffect.class),
|
||||||
|
Goad (GoadEffect.class),
|
||||||
|
Haunt (HauntEffect.class),
|
||||||
LookAt (LookAtEffect.class),
|
LookAt (LookAtEffect.class),
|
||||||
LoseLife (LifeLoseEffect.class),
|
LoseLife (LifeLoseEffect.class),
|
||||||
LosesGame (GameLossEffect.class),
|
LosesGame (GameLossEffect.class),
|
||||||
@@ -146,14 +148,13 @@ public enum ApiType {
|
|||||||
|
|
||||||
InternalEtbReplacement (ETBReplacementEffect.class),
|
InternalEtbReplacement (ETBReplacementEffect.class),
|
||||||
InternalLegendaryRule (CharmEffect.class),
|
InternalLegendaryRule (CharmEffect.class),
|
||||||
InternalHaunt (CharmEffect.class),
|
|
||||||
InternalIgnoreEffect (CharmEffect.class);
|
InternalIgnoreEffect (CharmEffect.class);
|
||||||
|
|
||||||
|
|
||||||
private final SpellAbilityEffect instanceEffect;
|
private final SpellAbilityEffect instanceEffect;
|
||||||
private final Class<? extends SpellAbilityEffect> clsEffect;
|
private final Class<? extends SpellAbilityEffect> clsEffect;
|
||||||
|
|
||||||
private static final Map<String, ApiType> allValues = new TreeMap<String, ApiType>(String.CASE_INSENSITIVE_ORDER);
|
private static final Map<String, ApiType> allValues = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
ApiType(Class<? extends SpellAbilityEffect> clsEf) { this(clsEf, true); }
|
ApiType(Class<? extends SpellAbilityEffect> clsEf) { this(clsEf, true); }
|
||||||
ApiType(Class<? extends SpellAbilityEffect> clsEf, final boolean isStateLess) {
|
ApiType(Class<? extends SpellAbilityEffect> clsEf, final boolean isStateLess) {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
|
public class HauntEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card card = sa.getHostCard();
|
||||||
|
if (sa.usesTargeting() && !card.isToken()) {
|
||||||
|
// haunt target but only if card is no token
|
||||||
|
final Card copy = card.getGame().getAction().exile(card);
|
||||||
|
sa.getTargets().getFirstTargetedCard().addHauntedBy(copy);
|
||||||
|
} else if (!sa.usesTargeting() && card.getHaunting() != null) {
|
||||||
|
// unhaunt
|
||||||
|
card.getHaunting().removeHauntedBy(card);
|
||||||
|
card.setHaunting(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1686,7 +1686,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
// Triggered abilities
|
// Triggered abilities
|
||||||
for (final Trigger trig : state.getTriggers()) {
|
for (final Trigger trig : state.getTriggers()) {
|
||||||
if (!trig.isSecondary()) {
|
if (!trig.isSecondary()) {
|
||||||
sb.append(trig.toString().replaceAll("\\\\r\\\\n", "\r\n")).append("\r\n");
|
sb.append(trig.replaceAbilityText(trig.toString(), null).replaceAll("\\\\r\\\\n", "\r\n")).append("\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1793,7 +1793,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
// Triggered abilities
|
// Triggered abilities
|
||||||
for (final Trigger trig : state.getTriggers()) {
|
for (final Trigger trig : state.getTriggers()) {
|
||||||
if (!trig.isSecondary()) {
|
if (!trig.isSecondary()) {
|
||||||
sb.append(trig.toString()).append("\r\n");
|
sb.append(trig.replaceAbilityText(trig.toString(), null)).append("\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6269,12 +6269,15 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
public final boolean isHauntedBy(Card c) {
|
public final boolean isHauntedBy(Card c) {
|
||||||
return FCollection.hasElement(hauntedBy, c);
|
return FCollection.hasElement(hauntedBy, c);
|
||||||
}
|
}
|
||||||
public final void addHauntedBy(Card c) {
|
public final void addHauntedBy(Card c, final boolean update) {
|
||||||
hauntedBy = view.addCard(hauntedBy, c, TrackableProperty.HauntedBy);
|
hauntedBy = view.addCard(hauntedBy, c, TrackableProperty.HauntedBy);
|
||||||
if (c != null) {
|
if (c != null && update) {
|
||||||
c.setHaunting(this);
|
c.setHaunting(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public final void addHauntedBy(Card c) {
|
||||||
|
addHauntedBy(c, true);
|
||||||
|
}
|
||||||
public final void removeHauntedBy(Card c) {
|
public final void removeHauntedBy(Card c) {
|
||||||
hauntedBy = view.removeCard(hauntedBy, c, TrackableProperty.HauntedBy);
|
hauntedBy = view.removeCard(hauntedBy, c, TrackableProperty.HauntedBy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
@@ -59,7 +60,6 @@ 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.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.Ability;
|
|
||||||
import forge.game.spellability.AbilityStatic;
|
import forge.game.spellability.AbilityStatic;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.OptionalCost;
|
import forge.game.spellability.OptionalCost;
|
||||||
@@ -2230,7 +2230,8 @@ public class CardFactoryUtil {
|
|||||||
card.getSpellAbilities().getFirst().setDelve(true);
|
card.getSpellAbilities().getFirst().setDelve(true);
|
||||||
}
|
}
|
||||||
else if (keyword.startsWith("Haunt")) {
|
else if (keyword.startsWith("Haunt")) {
|
||||||
setupHauntSpell(card);
|
addSpellAbility(keyword, card, null);
|
||||||
|
addTriggerAbility(keyword, card, null);
|
||||||
}
|
}
|
||||||
else if (keyword.startsWith("Annihilator")) {
|
else if (keyword.startsWith("Annihilator")) {
|
||||||
addTriggerAbility(keyword, card, null);
|
addTriggerAbility(keyword, card, null);
|
||||||
@@ -2836,6 +2837,100 @@ public class CardFactoryUtil {
|
|||||||
if (!intrinsic) {
|
if (!intrinsic) {
|
||||||
kws.addTrigger(cardTrigger);
|
kws.addTrigger(cardTrigger);
|
||||||
}
|
}
|
||||||
|
} else if (keyword.startsWith("Haunt")) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
final String hauntSVarName = k[1];
|
||||||
|
|
||||||
|
List<Trigger> cardTriggers = Lists.newArrayList();
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
if (card.isCreature()) {
|
||||||
|
sb.append("When ").append(card.getName());
|
||||||
|
sb.append(" enters the battlefield or the creature it haunts dies, ");
|
||||||
|
} else {
|
||||||
|
sb.append("When the creature ").append(card.getName());
|
||||||
|
sb.append(" haunts dies, ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// use new feature to get the Ability
|
||||||
|
sb.append("ABILITY");
|
||||||
|
|
||||||
|
final String hauntDescription = sb.toString();
|
||||||
|
|
||||||
|
// Second, create the trigger that runs when the haunted creature dies
|
||||||
|
final StringBuilder sbDies = new StringBuilder();
|
||||||
|
sbDies.append("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Exile |");
|
||||||
|
sbDies.append("ValidCard$ Creature.HauntedBy | Execute$ ").append(hauntSVarName);
|
||||||
|
sbDies.append(" | TriggerDescription$ ").append(hauntDescription);
|
||||||
|
|
||||||
|
final Trigger hauntedDies = TriggerHandler.parseTrigger(sbDies.toString(), card, intrinsic);
|
||||||
|
|
||||||
|
// Fourth, create a trigger that removes the haunting status if the
|
||||||
|
// haunter leaves the exile
|
||||||
|
final StringBuilder sbUnExiled = new StringBuilder();
|
||||||
|
sbUnExiled.append("Mode$ ChangesZone | Origin$ Exile | Destination$ Any | ");
|
||||||
|
sbUnExiled.append("ValidCard$ Card.Self | Static$ True | Secondary$ True | ");
|
||||||
|
sbUnExiled.append("TriggerDescription$ Blank");
|
||||||
|
|
||||||
|
final Trigger haunterUnExiled = TriggerHandler.parseTrigger(sbUnExiled.toString(), card,
|
||||||
|
intrinsic);
|
||||||
|
|
||||||
|
final SpellAbility unhaunt = AbilityFactory.getAbility("DB$ Haunt", card);
|
||||||
|
|
||||||
|
haunterUnExiled.setOverridingAbility(unhaunt);
|
||||||
|
|
||||||
|
cardTriggers.add(card.addTrigger(haunterUnExiled));
|
||||||
|
|
||||||
|
// Trigger for when the haunted creature leaves the battlefield
|
||||||
|
final StringBuilder sbHauntRemoved = new StringBuilder();
|
||||||
|
sbUnExiled.append("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ");
|
||||||
|
sbUnExiled.append("ValidCard$ Creature.HauntedBy | Static$ True | Secondary$ True | ");
|
||||||
|
sbUnExiled.append("TriggerDescription$ Blank");
|
||||||
|
|
||||||
|
final Trigger trigHauntRemoved = TriggerHandler.parseTrigger(sbHauntRemoved.toString(), card,
|
||||||
|
intrinsic);
|
||||||
|
trigHauntRemoved.setOverridingAbility(unhaunt);
|
||||||
|
|
||||||
|
cardTriggers.add(card.addTrigger(trigHauntRemoved));
|
||||||
|
|
||||||
|
// Fifth, add all triggers and abilities to the card.
|
||||||
|
if (card.isCreature()) {
|
||||||
|
// Third, create the trigger that runs when the haunting creature
|
||||||
|
// enters the battlefield
|
||||||
|
final StringBuilder sbETB = new StringBuilder();
|
||||||
|
sbETB.append("Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ");
|
||||||
|
sbETB.append(hauntSVarName).append(" | Secondary$ True | TriggerDescription$ ");
|
||||||
|
sbETB.append(hauntDescription);
|
||||||
|
|
||||||
|
final Trigger haunterETB = TriggerHandler.parseTrigger(sbETB.toString(), card, intrinsic);
|
||||||
|
|
||||||
|
cardTriggers.add(card.addTrigger(haunterETB));
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, create trigger that runs when the haunter goes to the
|
||||||
|
// graveyard
|
||||||
|
final StringBuilder sbHaunter = new StringBuilder();
|
||||||
|
sbHaunter.append("Mode$ ChangesZone | Origin$ ");
|
||||||
|
sbHaunter.append(card.isCreature() ? "Battlefield" : "Stack");
|
||||||
|
sbHaunter.append(" | Destination$ Graveyard | ValidCard$ Card.Self");
|
||||||
|
sbHaunter.append(" | Static$ True | Secondary$ True | TriggerDescription$ Blank");
|
||||||
|
|
||||||
|
final Trigger haunterDies = TriggerHandler.parseTrigger(sbHaunter.toString(), card, intrinsic);
|
||||||
|
|
||||||
|
final String hauntDiesEffectStr = "DB$ Haunt | ValidTgts$ Creature | TgtPrompt$ Choose target creature to haunt";
|
||||||
|
final SpellAbility hauntDiesAbility = AbilityFactory.getAbility(hauntDiesEffectStr, card);
|
||||||
|
|
||||||
|
haunterDies.setOverridingAbility(hauntDiesAbility);
|
||||||
|
|
||||||
|
cardTriggers.add(card.addTrigger(haunterDies));
|
||||||
|
|
||||||
|
cardTriggers.add(card.addTrigger(hauntedDies));
|
||||||
|
|
||||||
|
if (!intrinsic) {
|
||||||
|
for (final Trigger cardTrigger : cardTriggers) {
|
||||||
|
kws.addTrigger(cardTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (keyword.equals("Ingest")) {
|
} else if (keyword.equals("Ingest")) {
|
||||||
final String trigStr = "Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True"
|
final String trigStr = "Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True"
|
||||||
+ "| Secondary$ True | TriggerZones$ Battlefield | TriggerDescription$ Ingest ("
|
+ "| Secondary$ True | TriggerZones$ Battlefield | TriggerDescription$ Ingest ("
|
||||||
@@ -3476,6 +3571,19 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
card.addSpellAbility(newSA);
|
card.addSpellAbility(newSA);
|
||||||
|
} else if (keyword.startsWith("Haunt")) {
|
||||||
|
if (!card.isCreature() && intrinsic) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
final String hauntSVarName = k[1];
|
||||||
|
|
||||||
|
// no nice way to get the cost
|
||||||
|
String abString = card.getSVar(hauntSVarName).replace("DB$", "SP$");
|
||||||
|
abString += " | Cost$ 0 | StackDescription$ SpellDescription";
|
||||||
|
|
||||||
|
final SpellAbility sa = AbilityFactory.getAbility(abString, card);
|
||||||
|
sa.setPayCosts(new Cost(card.getManaCost(), false));
|
||||||
|
card.addSpellAbility(sa);
|
||||||
|
}
|
||||||
} else if (keyword.startsWith("Monstrosity")) {
|
} else if (keyword.startsWith("Monstrosity")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String magnitude = k[1];
|
final String magnitude = k[1];
|
||||||
@@ -3797,129 +3905,6 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Write javadoc for this method.
|
|
||||||
* @param card
|
|
||||||
*/
|
|
||||||
private static void setupHauntSpell(final Card card) {
|
|
||||||
final int hauntPos = card.getKeywordPosition("Haunt");
|
|
||||||
final String[] splitKeyword = card.getKeywords().get(hauntPos).split(":");
|
|
||||||
final String hauntSVarName = splitKeyword[1];
|
|
||||||
final String abilityDescription = splitKeyword[2];
|
|
||||||
final String hauntAbilityDescription = abilityDescription.substring(0, 1).toLowerCase()
|
|
||||||
+ abilityDescription.substring(1);
|
|
||||||
String hauntDescription;
|
|
||||||
if (card.isCreature()) {
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("When ").append(card.getName());
|
|
||||||
sb.append(" enters the battlefield or the creature it haunts dies, ");
|
|
||||||
sb.append(hauntAbilityDescription);
|
|
||||||
hauntDescription = sb.toString();
|
|
||||||
} else {
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("When the creature ").append(card.getName());
|
|
||||||
sb.append(" haunts dies, ").append(hauntAbilityDescription);
|
|
||||||
hauntDescription = sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
card.getKeywords().remove(hauntPos);
|
|
||||||
|
|
||||||
// First, create trigger that runs when the haunter goes to the
|
|
||||||
// graveyard
|
|
||||||
final StringBuilder sbHaunter = new StringBuilder();
|
|
||||||
sbHaunter.append("Mode$ ChangesZone | Origin$ Battlefield | ");
|
|
||||||
sbHaunter.append("Destination$ Graveyard | ValidCard$ Card.Self | ");
|
|
||||||
sbHaunter.append("Static$ True | Secondary$ True | TriggerDescription$ Blank");
|
|
||||||
|
|
||||||
final Trigger haunterDies = TriggerHandler.parseTrigger(sbHaunter.toString(), card, true);
|
|
||||||
|
|
||||||
final Ability haunterDiesWork = new Ability(card, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
this.getTargets().getFirstTargetedCard().addHauntedBy(card);
|
|
||||||
card.getGame().getAction().exile(card);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
haunterDiesWork.setDescription(hauntDescription);
|
|
||||||
haunterDiesWork.setTargetRestrictions(new TargetRestrictions(null, new String[]{"Creature"}, "1", "1")); // not null to make stack preserve targets set
|
|
||||||
|
|
||||||
final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
final Game game = card.getGame();
|
|
||||||
this.setActivatingPlayer(card.getController());
|
|
||||||
haunterDiesWork.setActivatingPlayer(card.getController());
|
|
||||||
CardCollection allCreatures = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
|
||||||
final CardCollection creats = CardLists.getTargetableCards(allCreatures, haunterDiesWork);
|
|
||||||
if (creats.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card toHaunt = card.getController().getController().chooseSingleEntityForEffect(creats, new SpellAbility.EmptySa(ApiType.InternalHaunt, card), "Choose target creature to haunt.");
|
|
||||||
haunterDiesWork.setTargetCard(toHaunt);
|
|
||||||
haunterDiesWork.setActivatingPlayer(card.getController());
|
|
||||||
game.getStack().add(haunterDiesWork);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
haunterDies.setOverridingAbility(haunterDiesSetup);
|
|
||||||
|
|
||||||
// Second, create the trigger that runs when the haunted creature dies
|
|
||||||
final StringBuilder sbDies = new StringBuilder();
|
|
||||||
sbDies.append("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ");
|
|
||||||
sbDies.append("ValidCard$ Creature.HauntedBy | Execute$ ").append(hauntSVarName);
|
|
||||||
sbDies.append(" | TriggerDescription$ ").append(hauntDescription);
|
|
||||||
|
|
||||||
final Trigger hauntedDies = forge.game.trigger.TriggerHandler.parseTrigger(sbDies.toString(), card, true);
|
|
||||||
|
|
||||||
// Third, create the trigger that runs when the haunting creature
|
|
||||||
// enters the battlefield
|
|
||||||
final StringBuilder sbETB = new StringBuilder();
|
|
||||||
sbETB.append("Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ");
|
|
||||||
sbETB.append(hauntSVarName).append(" | Secondary$ True | TriggerDescription$ ");
|
|
||||||
sbETB.append(hauntDescription);
|
|
||||||
|
|
||||||
final Trigger haunterETB = forge.game.trigger.TriggerHandler.parseTrigger(sbETB.toString(), card, true);
|
|
||||||
|
|
||||||
// Fourth, create a trigger that removes the haunting status if the
|
|
||||||
// haunter leaves the exile
|
|
||||||
final StringBuilder sbUnExiled = new StringBuilder();
|
|
||||||
sbUnExiled.append("Mode$ ChangesZone | Origin$ Exile | Destination$ Any | ");
|
|
||||||
sbUnExiled.append("ValidCard$ Card.Self | Static$ True | Secondary$ True | ");
|
|
||||||
sbUnExiled.append("TriggerDescription$ Blank");
|
|
||||||
|
|
||||||
final Trigger haunterUnExiled = forge.game.trigger.TriggerHandler.parseTrigger(sbUnExiled.toString(), card,
|
|
||||||
true);
|
|
||||||
|
|
||||||
final Ability haunterUnExiledWork = new Ability(card, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
if (card.getHaunting() != null) {
|
|
||||||
card.getHaunting().removeHauntedBy(card);
|
|
||||||
card.setHaunting(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
haunterUnExiled.setOverridingAbility(haunterUnExiledWork);
|
|
||||||
|
|
||||||
// Fifth, add all triggers and abilities to the card.
|
|
||||||
if (card.isCreature()) {
|
|
||||||
card.addTrigger(haunterETB);
|
|
||||||
card.addTrigger(haunterDies);
|
|
||||||
} else {
|
|
||||||
final String abString = card.getSVar(hauntSVarName).replace("AB$", "SP$")
|
|
||||||
+ " | SpellDescription$ " + abilityDescription;
|
|
||||||
|
|
||||||
final SpellAbility sa = AbilityFactory.getAbility(abString, card);
|
|
||||||
sa.setPayCosts(new Cost(card.getManaCost(), false));
|
|
||||||
card.addSpellAbility(sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
card.addTrigger(hauntedDies);
|
|
||||||
card.addTrigger(haunterUnExiled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this method.
|
* TODO: Write javadoc for this method.
|
||||||
* @param card
|
* @param card
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ public final class CardUtil {
|
|||||||
newCopy.setClones(in.getClones());
|
newCopy.setClones(in.getClones());
|
||||||
newCopy.setHaunting(in.getHaunting());
|
newCopy.setHaunting(in.getHaunting());
|
||||||
for (final Card haunter : in.getHauntedBy()) {
|
for (final Card haunter : in.getHauntedBy()) {
|
||||||
newCopy.addHauntedBy(haunter);
|
newCopy.addHauntedBy(haunter, false);
|
||||||
}
|
}
|
||||||
for (final Object o : in.getRemembered()) {
|
for (final Object o : in.getRemembered()) {
|
||||||
newCopy.addRemembered(o);
|
newCopy.addRemembered(o);
|
||||||
|
|||||||
@@ -620,26 +620,6 @@ public class CardView extends GameEntityView {
|
|||||||
sb.append("]\r\n");
|
sb.append("]\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<CardView> hauntedBy = getHauntedBy();
|
|
||||||
if (hauntedBy != null) {
|
|
||||||
sb.append("Haunted by: ");
|
|
||||||
boolean needDelim = false;
|
|
||||||
for (final CardView c : hauntedBy) {
|
|
||||||
if (needDelim) {
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
else { needDelim = false; }
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
sb.append("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
CardView haunting = getHaunting();
|
|
||||||
if (haunting != null) {
|
|
||||||
sb.append("Haunting: ").append(haunting);
|
|
||||||
sb.append("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
CardView pairedWith = getPairedWith();
|
CardView pairedWith = getPairedWith();
|
||||||
if (pairedWith != null) {
|
if (pairedWith != null) {
|
||||||
sb.append("\r\n \r\nPaired With: ").append(pairedWith);
|
sb.append("\r\n \r\nPaired With: ").append(pairedWith);
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ package forge.game.trigger;
|
|||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.TriggerReplacementBase;
|
import forge.game.TriggerReplacementBase;
|
||||||
|
import forge.game.ability.AbilityFactory;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
|
import forge.game.ability.effects.CharmEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
@@ -158,6 +161,51 @@ public abstract class Trigger extends TriggerReplacementBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final String replaceAbilityText(final String desc, SpellAbility sa) {
|
||||||
|
String result = desc;
|
||||||
|
|
||||||
|
// this function is for ABILITY
|
||||||
|
if (!result.contains("ABILITY")) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it has already sa, used in WrappedAbility
|
||||||
|
if (sa == null) {
|
||||||
|
sa = getOverridingAbility();
|
||||||
|
}
|
||||||
|
if (sa == null && this.mapParams.containsKey("Execute")) {
|
||||||
|
sa = AbilityFactory.getAbility(hostCard, this.mapParams.get("Execute"));
|
||||||
|
}
|
||||||
|
if (sa != null) {
|
||||||
|
String saDesc;
|
||||||
|
// if sa is a wrapper, get the Wrapped Ability
|
||||||
|
if (sa.isWrapper()) {
|
||||||
|
final WrappedAbility wa = (WrappedAbility) sa;
|
||||||
|
sa = wa.getWrappedAbility();
|
||||||
|
|
||||||
|
// wrapped Charm spells are special,
|
||||||
|
// only get the selected abilities
|
||||||
|
if (ApiType.Charm.equals(sa.getApi())) {
|
||||||
|
saDesc = sa.getStackDescription();
|
||||||
|
} else {
|
||||||
|
saDesc = sa.getDescription();
|
||||||
|
}
|
||||||
|
} else if (ApiType.Charm.equals(sa.getApi())) {
|
||||||
|
// use special formating, can be used in Card Description
|
||||||
|
saDesc = CharmEffect.makeFormatedDescription(sa);
|
||||||
|
} else {
|
||||||
|
saDesc = sa.getDescription();
|
||||||
|
}
|
||||||
|
if (!saDesc.isEmpty()) {
|
||||||
|
// string might have leading whitespace
|
||||||
|
saDesc = saDesc.trim();
|
||||||
|
saDesc = saDesc.substring(0, 1).toLowerCase() + saDesc.substring(1);
|
||||||
|
result = result.replace("ABILITY", saDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* phasesCheck.
|
* phasesCheck.
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ public class TriggerHandler {
|
|||||||
wrapperAbility.setTrigger(true);
|
wrapperAbility.setTrigger(true);
|
||||||
wrapperAbility.setMandatory(isMandatory);
|
wrapperAbility.setMandatory(isMandatory);
|
||||||
//wrapperAbility.setDescription(wrapperAbility.getStackDescription());
|
//wrapperAbility.setDescription(wrapperAbility.getStackDescription());
|
||||||
wrapperAbility.setDescription(wrapperAbility.toUnsuppressedString());
|
//wrapperAbility.setDescription(wrapperAbility.toUnsuppressedString());
|
||||||
|
|
||||||
wrapperAbility.setLastStateBattlefield(game.getLastStateBattlefield());
|
wrapperAbility.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||||
if (regtrig.isStatic()) {
|
if (regtrig.isStatic()) {
|
||||||
|
|||||||
@@ -196,8 +196,8 @@ public class WrappedAbility extends Ability {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStackDescription() {
|
public String getStackDescription() {
|
||||||
final StringBuilder sb = new StringBuilder(toUnsuppressedString());
|
final StringBuilder sb = new StringBuilder(regtrig.replaceAbilityText(toUnsuppressedString(), this));
|
||||||
if (this.getTargetRestrictions() != null) {
|
if (usesTargeting()) {
|
||||||
sb.append(" (Targeting ");
|
sb.append(" (Targeting ");
|
||||||
for (final GameObject o : this.getTargets().getTargets()) {
|
for (final GameObject o : this.getTargets().getTargets()) {
|
||||||
sb.append(o.toString());
|
sb.append(o.toString());
|
||||||
@@ -271,26 +271,26 @@ public class WrappedAbility extends Ability {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSvarWithFallback(String name) {
|
public String getSvarWithFallback(String name) {
|
||||||
return sa.getSvarWithFallback(name);
|
return sa.getSvarWithFallback(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSVar(String name) {
|
public String getSVar(String name) {
|
||||||
return sa.getSVar(name);
|
return sa.getSVar(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getSVarInt(String name) {
|
public Integer getSVarInt(String name) {
|
||||||
return sa.getSVarInt(name);
|
return sa.getSVarInt(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getSVars() {
|
public Set<String> getSVars() {
|
||||||
return sa.getSVars();
|
return sa.getSVars();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetOnceResolved() {
|
public void resetOnceResolved() {
|
||||||
// Fixing an issue with Targeting + Paying Mana
|
// Fixing an issue with Targeting + Paying Mana
|
||||||
// sa.resetOnceResolved();
|
// sa.resetOnceResolved();
|
||||||
|
|||||||
@@ -41,11 +41,8 @@ import forge.game.ability.AbilityFactory;
|
|||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.event.EventValueChangeType;
|
import forge.game.event.EventValueChangeType;
|
||||||
import forge.game.event.GameEventCardStatsChanged;
|
import forge.game.event.GameEventCardStatsChanged;
|
||||||
@@ -58,7 +55,6 @@ import forge.game.player.PlayerController.ManaPaymentPurpose;
|
|||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.Ability;
|
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.AbilityStatic;
|
import forge.game.spellability.AbilityStatic;
|
||||||
import forge.game.spellability.OptionalCost;
|
import forge.game.spellability.OptionalCost;
|
||||||
@@ -527,10 +523,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
|
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
|
||||||
finishResolving(sa, thisHasFizzled);
|
finishResolving(sa, thisHasFizzled);
|
||||||
|
|
||||||
if (source.hasStartOfKeyword("Haunt") && !source.isCreature() && game.getZoneOf(source).is(ZoneType.Graveyard)) {
|
|
||||||
handleHauntForNonPermanents(sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
game.copyLastState();
|
game.copyLastState();
|
||||||
// FIXME: assuming that if the stack is empty, no reason to hold on to old LKI data (everything is a new object). Is this correct?
|
// FIXME: assuming that if the stack is empty, no reason to hold on to old LKI data (everything is a new object). Is this correct?
|
||||||
@@ -538,32 +530,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHauntForNonPermanents(final SpellAbility sa) {
|
|
||||||
final Card source = sa.getHostCard();
|
|
||||||
final CardCollection creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
|
||||||
final Ability haunterDiesWork = new Ability(source, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
game.getAction().exile(source);
|
|
||||||
getTargetCard().addHauntedBy(source);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (int i = 0; i < creats.size(); i++) {
|
|
||||||
haunterDiesWork.setActivatingPlayer(sa.getActivatingPlayer());
|
|
||||||
if (!creats.get(i).canBeTargetedBy(haunterDiesWork)) {
|
|
||||||
creats.remove(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!creats.isEmpty()) {
|
|
||||||
haunterDiesWork.setDescription("");
|
|
||||||
haunterDiesWork.setTargetRestrictions(new TargetRestrictions("", "Creature".split(" "), "1", "1"));
|
|
||||||
final Card targetCard = source.getController().getController().chooseSingleEntityForEffect(creats, new SpellAbility.EmptySa(ApiType.InternalHaunt, source), "Choose target creature to haunt.");
|
|
||||||
haunterDiesWork.setTargetCard(targetCard);
|
|
||||||
add(haunterDiesWork);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final void finishResolving(final SpellAbility sa, final boolean fizzle) {
|
private final void finishResolving(final SpellAbility sa, final boolean fizzle) {
|
||||||
// remove SA and card from the stack
|
// remove SA and card from the stack
|
||||||
removeCardFromStack(sa, fizzle);
|
removeCardFromStack(sa, fizzle);
|
||||||
|
|||||||
Reference in New Issue
Block a user