mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
CardFactoryUtil - extracted the longest keyword-to-ability conversions into separate methods
removed keyword 'CARDNAME is (color)' because this data is already contained in a mandatory field named 'color' renamed postFactoryKeywords to setupKeywordedAbilities generally 'setup' means 'make all changes to card yourself' and 'make' intends only creation of spellability, at least 'make'-methods always return one.
This commit is contained in:
@@ -322,7 +322,7 @@ public class CardFactory {
|
|||||||
CardFactoryArtifacts.buildCard(card, cardName);
|
CardFactoryArtifacts.buildCard(card, cardName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CardFactoryUtil.postFactoryKeywords(card);
|
CardFactoryUtil.setupKeywordedAbilities(card);
|
||||||
} // getCard2
|
} // getCard2
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import forge.card.trigger.Trigger;
|
|||||||
import forge.card.trigger.TriggerHandler;
|
import forge.card.trigger.TriggerHandler;
|
||||||
import forge.card.trigger.TriggerType;
|
import forge.card.trigger.TriggerType;
|
||||||
import forge.control.input.Input;
|
import forge.control.input.Input;
|
||||||
|
import forge.control.input.InputSelectManyCards;
|
||||||
import forge.game.GameState;
|
import forge.game.GameState;
|
||||||
import forge.game.ai.ComputerUtil;
|
import forge.game.ai.ComputerUtil;
|
||||||
import forge.game.ai.ComputerUtilCard;
|
import forge.game.ai.ComputerUtilCard;
|
||||||
@@ -2470,7 +2471,7 @@ public class CardFactoryUtil {
|
|||||||
temp.setOwner(controller);
|
temp.setOwner(controller);
|
||||||
temp.setToken(true);
|
temp.setToken(true);
|
||||||
CardFactoryUtil.parseKeywords(temp, temp.getName());
|
CardFactoryUtil.parseKeywords(temp, temp.getName());
|
||||||
CardFactoryUtil.postFactoryKeywords(temp);
|
CardFactoryUtil.setupKeywordedAbilities(temp);
|
||||||
Singletons.getModel().getGame().getAction().moveToPlay(temp);
|
Singletons.getModel().getGame().getAction().moveToPlay(temp);
|
||||||
list.add(temp);
|
list.add(temp);
|
||||||
}
|
}
|
||||||
@@ -2789,7 +2790,7 @@ public class CardFactoryUtil {
|
|||||||
* @param card
|
* @param card
|
||||||
* a {@link forge.Card} object.
|
* a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static void postFactoryKeywords(final Card card) {
|
public static void setupKeywordedAbilities(final Card card) {
|
||||||
// this function should handle any keywords that need to be added after
|
// this function should handle any keywords that need to be added after
|
||||||
// a spell goes through the factory
|
// a spell goes through the factory
|
||||||
// Cards with Cycling abilities
|
// Cards with Cycling abilities
|
||||||
@@ -2826,46 +2827,9 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int evokeKeyword = CardFactoryUtil.hasKeyword(card, "Evoke");
|
final int evokePos = CardFactoryUtil.hasKeyword(card, "Evoke");
|
||||||
if (evokeKeyword != -1) {
|
if (evokePos != -1) {
|
||||||
final SpellAbility evokedSpell = new Spell(card) {
|
card.addSpellAbility(makeEvokeSpell(card, card.getKeyword().get(evokePos)));
|
||||||
private static final long serialVersionUID = -1598664196463358630L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
card.setEvoked(true);
|
|
||||||
Singletons.getModel().getGame().getAction().moveToPlay(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canPlayAI() {
|
|
||||||
if (!SpellPermanent.checkETBEffects(card, (AIPlayer) this.getActivatingPlayer())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.canPlayAI();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
final String parse = card.getKeyword().get(evokeKeyword).toString();
|
|
||||||
card.removeIntrinsicKeyword(parse);
|
|
||||||
|
|
||||||
final String[] k = parse.split(":");
|
|
||||||
final String evokedCost = k[1];
|
|
||||||
|
|
||||||
evokedSpell.setManaCost(new ManaCost(new ManaCostParser(evokedCost)));
|
|
||||||
|
|
||||||
final StringBuilder desc = new StringBuilder();
|
|
||||||
desc.append("Evoke ").append(evokedCost);
|
|
||||||
desc.append(" (You may cast this spell for its evoke cost. ");
|
|
||||||
desc.append("If you do, when it enters the battlefield, sacrifice it.)");
|
|
||||||
|
|
||||||
evokedSpell.setDescription(desc.toString());
|
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(card.getName()).append(" (Evoked)");
|
|
||||||
evokedSpell.setStackDescription(sb.toString());
|
|
||||||
evokedSpell.setBasicSpell(false);
|
|
||||||
|
|
||||||
card.addSpellAbility(evokedSpell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CardFactoryUtil.hasKeyword(card, "Cycling") != -1) {
|
if (CardFactoryUtil.hasKeyword(card, "Cycling") != -1) {
|
||||||
@@ -2911,7 +2875,7 @@ public class CardFactoryUtil {
|
|||||||
int shiftPos = CardFactoryUtil.hasKeyword(card, "Soulshift");
|
int shiftPos = CardFactoryUtil.hasKeyword(card, "Soulshift");
|
||||||
while (shiftPos != -1) {
|
while (shiftPos != -1) {
|
||||||
final int n = shiftPos;
|
final int n = shiftPos;
|
||||||
final String parse = card.getKeyword().get(n).toString();
|
final String parse = card.getKeyword().get(n);
|
||||||
final String[] k = parse.split(" ");
|
final String[] k = parse.split(" ");
|
||||||
final int manacost = Integer.parseInt(k[1]);
|
final int manacost = Integer.parseInt(k[1]);
|
||||||
|
|
||||||
@@ -2928,12 +2892,10 @@ public class CardFactoryUtil {
|
|||||||
shiftPos = CardFactoryUtil.hasKeyword(card, "Soulshift", n + 1);
|
shiftPos = CardFactoryUtil.hasKeyword(card, "Soulshift", n + 1);
|
||||||
} // Soulshift
|
} // Soulshift
|
||||||
|
|
||||||
if (CardFactoryUtil.hasKeyword(card, "Echo") != -1) {
|
final int echoPos = CardFactoryUtil.hasKeyword(card, "Echo");
|
||||||
final int n = CardFactoryUtil.hasKeyword(card, "Echo");
|
if (echoPos != -1) {
|
||||||
if (n != -1) {
|
|
||||||
final String parse = card.getKeyword().get(n).toString();
|
|
||||||
// card.removeIntrinsicKeyword(parse);
|
// card.removeIntrinsicKeyword(parse);
|
||||||
|
final String parse = card.getKeyword().get(echoPos);
|
||||||
final String[] k = parse.split(":");
|
final String[] k = parse.split(":");
|
||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
|
|
||||||
@@ -2950,7 +2912,6 @@ public class CardFactoryUtil {
|
|||||||
};
|
};
|
||||||
card.addComesIntoPlayCommand(intoPlay);
|
card.addComesIntoPlayCommand(intoPlay);
|
||||||
|
|
||||||
}
|
|
||||||
} // echo
|
} // echo
|
||||||
|
|
||||||
if (CardFactoryUtil.hasKeyword(card, "Suspend") != -1) {
|
if (CardFactoryUtil.hasKeyword(card, "Suspend") != -1) {
|
||||||
@@ -2975,22 +2936,6 @@ public class CardFactoryUtil {
|
|||||||
sa.setXManaCost(xCount);
|
sa.setXManaCost(xCount);
|
||||||
} // X
|
} // X
|
||||||
|
|
||||||
int cardnameSpot = CardFactoryUtil.hasKeyword(card, "CARDNAME is ");
|
|
||||||
if (cardnameSpot != -1) {
|
|
||||||
String color = "1";
|
|
||||||
while (cardnameSpot != -1) {
|
|
||||||
if (cardnameSpot != -1) {
|
|
||||||
final String parse = card.getKeyword().get(cardnameSpot).toString();
|
|
||||||
card.removeIntrinsicKeyword(parse);
|
|
||||||
color += " "
|
|
||||||
+ MagicColor.toShortString(parse.replace("CARDNAME is ", "").replace(".",
|
|
||||||
""));
|
|
||||||
cardnameSpot = CardFactoryUtil.hasKeyword(card, "CARDNAME is ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
card.addColor(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CardFactoryUtil.hasKeyword(card, "Fading") != -1) {
|
if (CardFactoryUtil.hasKeyword(card, "Fading") != -1) {
|
||||||
final int n = CardFactoryUtil.hasKeyword(card, "Fading");
|
final int n = CardFactoryUtil.hasKeyword(card, "Fading");
|
||||||
if (n != -1) {
|
if (n != -1) {
|
||||||
@@ -3029,63 +2974,7 @@ public class CardFactoryUtil {
|
|||||||
if (!card.getSVar("AltCost").equals("")) {
|
if (!card.getSVar("AltCost").equals("")) {
|
||||||
final SpellAbility[] abilities = card.getSpellAbility();
|
final SpellAbility[] abilities = card.getSpellAbility();
|
||||||
if ((abilities.length > 0) && abilities[0].isSpell()) {
|
if ((abilities.length > 0) && abilities[0].isSpell()) {
|
||||||
String altCost = card.getSVar("AltCost");
|
card.addSpellAbility(makeAltCost(card, abilities[0]));
|
||||||
final HashMap<String, String> mapParams = new HashMap<String, String>();
|
|
||||||
String altCostDescription = "";
|
|
||||||
final String[] altCosts = altCost.split("\\|");
|
|
||||||
|
|
||||||
for (int aCnt = 0; aCnt < altCosts.length; aCnt++) {
|
|
||||||
altCosts[aCnt] = altCosts[aCnt].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final String altCost2 : altCosts) {
|
|
||||||
final String[] aa = altCost2.split("\\$");
|
|
||||||
|
|
||||||
for (int aaCnt = 0; aaCnt < aa.length; aaCnt++) {
|
|
||||||
aa[aaCnt] = aa[aaCnt].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aa.length != 2) {
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("StaticEffectFactory Parsing Error: Split length of ");
|
|
||||||
sb.append(altCost2).append(" in ").append(card.getName()).append(" is not 2.");
|
|
||||||
throw new RuntimeException(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
mapParams.put(aa[0], aa[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
altCost = mapParams.get("Cost");
|
|
||||||
|
|
||||||
if (mapParams.containsKey("Description")) {
|
|
||||||
altCostDescription = mapParams.get("Description");
|
|
||||||
}
|
|
||||||
|
|
||||||
final SpellAbility sa = abilities[0];
|
|
||||||
final SpellAbility altCostSA = sa.copy();
|
|
||||||
|
|
||||||
final Cost abCost = new Cost(card, altCost, altCostSA.isAbility());
|
|
||||||
altCostSA.setPayCosts(abCost);
|
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
if (!altCostDescription.equals("")) {
|
|
||||||
sb.append(altCostDescription);
|
|
||||||
} else {
|
|
||||||
sb.append("You may ").append(abCost.toStringAlt());
|
|
||||||
sb.append(" rather than pay ").append(card.getName()).append("'s mana cost.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final SpellAbilityRestriction restriction = new SpellAbilityRestriction();
|
|
||||||
restriction.setRestrictions(mapParams);
|
|
||||||
if (!mapParams.containsKey("ActivationZone")) {
|
|
||||||
restriction.setZone(ZoneType.Hand);
|
|
||||||
}
|
|
||||||
altCostSA.setRestrictions(restriction);
|
|
||||||
altCostSA.setDescription(sb.toString());
|
|
||||||
altCostSA.setBasicSpell(false);
|
|
||||||
|
|
||||||
card.addSpellAbility(altCostSA);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3094,161 +2983,7 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (card.hasStartOfKeyword("Haunt")) {
|
if (card.hasStartOfKeyword("Haunt")) {
|
||||||
final int hauntPos = card.getKeywordPosition("Haunt");
|
setupHauntSpell(card);
|
||||||
final String[] splitKeyword = card.getKeyword().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.getKeyword().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 = forge.card.trigger.TriggerHandler
|
|
||||||
.parseTrigger(sbHaunter.toString(), card, true);
|
|
||||||
|
|
||||||
final Ability haunterDiesWork = new Ability(card, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
this.getTargetCard().addHauntedBy(card);
|
|
||||||
Singletons.getModel().getGame().getAction().exile(card);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
haunterDiesWork.setDescription(hauntDescription);
|
|
||||||
|
|
||||||
final Input target = new Input() {
|
|
||||||
private static final long serialVersionUID = 1981791992623774490L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMessage() {
|
|
||||||
CMatchUI.SINGLETON_INSTANCE.showMessage("Choose target creature to haunt.");
|
|
||||||
ButtonUtil.disableAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void selectCard(final Card c) {
|
|
||||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
|
||||||
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c.canBeTargetedBy(haunterDiesWork)) {
|
|
||||||
haunterDiesWork.setTargetCard(c);
|
|
||||||
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
|
|
||||||
this.stop();
|
|
||||||
} else {
|
|
||||||
CMatchUI.SINGLETON_INSTANCE
|
|
||||||
.showMessage("Cannot target this card (Shroud? Protection?).");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
final List<Card> creats = CardLists.filter(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
|
||||||
for (int i = 0; i < creats.size(); i++) {
|
|
||||||
if (!creats.get(i).canBeTargetedBy(this)) {
|
|
||||||
creats.remove(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (creats.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to do it this way because I don't know quite how to
|
|
||||||
// make TriggerHandler respect BeforePayMana.
|
|
||||||
if (card.getController().isHuman()) {
|
|
||||||
Singletons.getModel().getMatch().getInput().setInput(target);
|
|
||||||
} else {
|
|
||||||
// AI choosing what to haunt
|
|
||||||
final List<Card> oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent());
|
|
||||||
if (!oppCreats.isEmpty()) {
|
|
||||||
haunterDiesWork.setTargetCard(ComputerUtilCard.getWorstCreatureAI(oppCreats));
|
|
||||||
} else {
|
|
||||||
haunterDiesWork.setTargetCard(ComputerUtilCard.getWorstCreatureAI(creats));
|
|
||||||
}
|
|
||||||
Singletons.getModel().getGame().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.card.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.card.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.card.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$")
|
|
||||||
.replace("Cost$ 0", "Cost$ " + card.getManaCost())
|
|
||||||
+ " | SpellDescription$ " + abilityDescription;
|
|
||||||
|
|
||||||
final SpellAbility sa = AbilityFactory.getAbility(abString, card);
|
|
||||||
card.addSpellAbility(sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
card.addTrigger(hauntedDies);
|
|
||||||
card.addTrigger(haunterUnExiled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.hasKeyword("Provoke")) {
|
if (card.hasKeyword("Provoke")) {
|
||||||
@@ -3293,57 +3028,7 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (card.hasKeyword("Epic")) {
|
if (card.hasKeyword("Epic")) {
|
||||||
final SpellAbility origSA = card.getSpellAbilities().get(0);
|
final SpellAbility newSA = makeEpic(card);
|
||||||
|
|
||||||
final SpellAbility newSA = new Spell(card, origSA.getPayCosts(), origSA.getTarget()) {
|
|
||||||
private static final long serialVersionUID = -7934420043356101045L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resolve() {
|
|
||||||
final GameState game = Singletons.getModel().getGame();
|
|
||||||
|
|
||||||
String name = card.toString() + " Epic";
|
|
||||||
if (card.getController().getCardsIn(ZoneType.Battlefield, name).isEmpty()) {
|
|
||||||
// Create Epic emblem
|
|
||||||
final Card eff = new Card();
|
|
||||||
eff.setName(card.toString() + " Epic");
|
|
||||||
eff.addType("Effect"); // Or Emblem
|
|
||||||
eff.setToken(true); // Set token to true, so when leaving
|
|
||||||
// play it gets nuked
|
|
||||||
eff.addController(card.getController());
|
|
||||||
eff.setOwner(card.getController());
|
|
||||||
eff.setColor(card.getColor());
|
|
||||||
eff.setImmutable(true);
|
|
||||||
eff.setEffectSource(card);
|
|
||||||
|
|
||||||
eff.addStaticAbility("Mode$ CantBeCast | ValidCard$ Card | Caster$ You "
|
|
||||||
+ "| Description$ For the rest of the game, you can't cast spells.");
|
|
||||||
|
|
||||||
eff.setSVar("EpicCopy", "AB$ CopySpellAbility | Cost$ 0 | Defined$ EffectSource");
|
|
||||||
|
|
||||||
final Trigger copyTrigger = forge.card.trigger.TriggerHandler.parseTrigger(
|
|
||||||
"Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ EpicCopy | TriggerDescription$ "
|
|
||||||
+ "At the beginning of each of your upkeeps, copy " + card.toString()
|
|
||||||
+ " except for its epic ability.", eff, false);
|
|
||||||
|
|
||||||
eff.addTrigger(copyTrigger);
|
|
||||||
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
|
||||||
game.getAction().moveToPlay(eff);
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (card.getController().isHuman()) {
|
|
||||||
game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA, false);
|
|
||||||
} else {
|
|
||||||
ComputerUtil.playNoStack((AIPlayer)card.getController(), origSA, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
newSA.setDescription(origSA.getDescription());
|
|
||||||
|
|
||||||
origSA.setPayCosts(null);
|
|
||||||
origSA.setManaCost(ManaCost.ZERO);
|
|
||||||
|
|
||||||
card.clearSpellAbility();
|
card.clearSpellAbility();
|
||||||
card.addSpellAbility(newSA);
|
card.addSpellAbility(newSA);
|
||||||
@@ -3470,6 +3155,14 @@ public class CardFactoryUtil {
|
|||||||
card.getIntrinsicAbilities().add(abilityStr.toString());
|
card.getIntrinsicAbilities().add(abilityStr.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupEtbKeywords(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param card
|
||||||
|
*/
|
||||||
|
private static void setupEtbKeywords(final Card card) {
|
||||||
for (String kw : card.getKeyword()) {
|
for (String kw : card.getKeyword()) {
|
||||||
|
|
||||||
if (kw.startsWith("ETBReplacement")) {
|
if (kw.startsWith("ETBReplacement")) {
|
||||||
@@ -3550,6 +3243,332 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param card
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static SpellAbility makeEpic(final Card card) {
|
||||||
|
final SpellAbility origSA = card.getSpellAbilities().get(0);
|
||||||
|
|
||||||
|
final SpellAbility newSA = new Spell(card, origSA.getPayCosts(), origSA.getTarget()) {
|
||||||
|
private static final long serialVersionUID = -7934420043356101045L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve() {
|
||||||
|
final GameState game = Singletons.getModel().getGame();
|
||||||
|
|
||||||
|
String name = card.toString() + " Epic";
|
||||||
|
if (card.getController().getCardsIn(ZoneType.Battlefield, name).isEmpty()) {
|
||||||
|
// Create Epic emblem
|
||||||
|
final Card eff = new Card();
|
||||||
|
eff.setName(card.toString() + " Epic");
|
||||||
|
eff.addType("Effect"); // Or Emblem
|
||||||
|
eff.setToken(true); // Set token to true, so when leaving
|
||||||
|
// play it gets nuked
|
||||||
|
eff.addController(card.getController());
|
||||||
|
eff.setOwner(card.getController());
|
||||||
|
eff.setColor(card.getColor());
|
||||||
|
eff.setImmutable(true);
|
||||||
|
eff.setEffectSource(card);
|
||||||
|
|
||||||
|
eff.addStaticAbility("Mode$ CantBeCast | ValidCard$ Card | Caster$ You "
|
||||||
|
+ "| Description$ For the rest of the game, you can't cast spells.");
|
||||||
|
|
||||||
|
eff.setSVar("EpicCopy", "AB$ CopySpellAbility | Cost$ 0 | Defined$ EffectSource");
|
||||||
|
|
||||||
|
final Trigger copyTrigger = forge.card.trigger.TriggerHandler.parseTrigger(
|
||||||
|
"Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ EpicCopy | TriggerDescription$ "
|
||||||
|
+ "At the beginning of each of your upkeeps, copy " + card.toString()
|
||||||
|
+ " except for its epic ability.", eff, false);
|
||||||
|
|
||||||
|
eff.addTrigger(copyTrigger);
|
||||||
|
|
||||||
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
|
game.getAction().moveToPlay(eff);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.getController().isHuman()) {
|
||||||
|
game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA, false);
|
||||||
|
} else {
|
||||||
|
ComputerUtil.playNoStack((AIPlayer)card.getController(), origSA, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
newSA.setDescription(origSA.getDescription());
|
||||||
|
|
||||||
|
origSA.setPayCosts(null);
|
||||||
|
origSA.setManaCost(ManaCost.ZERO);
|
||||||
|
return newSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.getKeyword().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.getKeyword().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 = forge.card.trigger.TriggerHandler
|
||||||
|
.parseTrigger(sbHaunter.toString(), card, true);
|
||||||
|
|
||||||
|
final Ability haunterDiesWork = new Ability(card, ManaCost.ZERO) {
|
||||||
|
@Override
|
||||||
|
public void resolve() {
|
||||||
|
this.getTargetCard().addHauntedBy(card);
|
||||||
|
Singletons.getModel().getGame().getAction().exile(card);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
haunterDiesWork.setDescription(hauntDescription);
|
||||||
|
|
||||||
|
final InputSelectManyCards target = new InputSelectManyCards(1,1) {
|
||||||
|
private static final long serialVersionUID = 1981791992623774490L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Input onDone() {
|
||||||
|
haunterDiesWork.setTargetCard(selected.get(0));
|
||||||
|
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isValidChoice(Card c) {
|
||||||
|
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||||
|
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return c.canBeTargetedBy(haunterDiesWork);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
target.setMessage("Choose target creature to haunt.");
|
||||||
|
|
||||||
|
final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
|
||||||
|
@Override
|
||||||
|
public void resolve() {
|
||||||
|
final List<Card> creats = CardLists.filter(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||||
|
for (int i = 0; i < creats.size(); i++) {
|
||||||
|
if (!creats.get(i).canBeTargetedBy(this)) {
|
||||||
|
creats.remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (creats.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to do it this way because I don't know quite how to
|
||||||
|
// make TriggerHandler respect BeforePayMana.
|
||||||
|
if (card.getController().isHuman()) {
|
||||||
|
Singletons.getModel().getMatch().getInput().setInput(target);
|
||||||
|
} else {
|
||||||
|
// AI choosing what to haunt
|
||||||
|
final List<Card> oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent());
|
||||||
|
if (!oppCreats.isEmpty()) {
|
||||||
|
haunterDiesWork.setTargetCard(ComputerUtilCard.getWorstCreatureAI(oppCreats));
|
||||||
|
} else {
|
||||||
|
haunterDiesWork.setTargetCard(ComputerUtilCard.getWorstCreatureAI(creats));
|
||||||
|
}
|
||||||
|
Singletons.getModel().getGame().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.card.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.card.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.card.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$")
|
||||||
|
.replace("Cost$ 0", "Cost$ " + card.getManaCost())
|
||||||
|
+ " | SpellDescription$ " + abilityDescription;
|
||||||
|
|
||||||
|
final SpellAbility sa = AbilityFactory.getAbility(abString, card);
|
||||||
|
card.addSpellAbility(sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
card.addTrigger(hauntedDies);
|
||||||
|
card.addTrigger(haunterUnExiled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param card
|
||||||
|
* @param abilities
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static SpellAbility makeAltCost(final Card card, final SpellAbility sa) {
|
||||||
|
String altCost = card.getSVar("AltCost");
|
||||||
|
final HashMap<String, String> mapParams = new HashMap<String, String>();
|
||||||
|
String altCostDescription = "";
|
||||||
|
final String[] altCosts = altCost.split("\\|");
|
||||||
|
|
||||||
|
for (int aCnt = 0; aCnt < altCosts.length; aCnt++) {
|
||||||
|
altCosts[aCnt] = altCosts[aCnt].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final String altCost2 : altCosts) {
|
||||||
|
final String[] aa = altCost2.split("\\$");
|
||||||
|
|
||||||
|
for (int aaCnt = 0; aaCnt < aa.length; aaCnt++) {
|
||||||
|
aa[aaCnt] = aa[aaCnt].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aa.length != 2) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("StaticEffectFactory Parsing Error: Split length of ");
|
||||||
|
sb.append(altCost2).append(" in ").append(card.getName()).append(" is not 2.");
|
||||||
|
throw new RuntimeException(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
mapParams.put(aa[0], aa[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
altCost = mapParams.get("Cost");
|
||||||
|
|
||||||
|
if (mapParams.containsKey("Description")) {
|
||||||
|
altCostDescription = mapParams.get("Description");
|
||||||
|
}
|
||||||
|
|
||||||
|
final SpellAbility altCostSA = sa.copy();
|
||||||
|
|
||||||
|
final Cost abCost = new Cost(card, altCost, altCostSA.isAbility());
|
||||||
|
altCostSA.setPayCosts(abCost);
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
if (!altCostDescription.equals("")) {
|
||||||
|
sb.append(altCostDescription);
|
||||||
|
} else {
|
||||||
|
sb.append("You may ").append(abCost.toStringAlt());
|
||||||
|
sb.append(" rather than pay ").append(card.getName()).append("'s mana cost.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final SpellAbilityRestriction restriction = new SpellAbilityRestriction();
|
||||||
|
restriction.setRestrictions(mapParams);
|
||||||
|
if (!mapParams.containsKey("ActivationZone")) {
|
||||||
|
restriction.setZone(ZoneType.Hand);
|
||||||
|
}
|
||||||
|
altCostSA.setRestrictions(restriction);
|
||||||
|
altCostSA.setDescription(sb.toString());
|
||||||
|
altCostSA.setBasicSpell(false);
|
||||||
|
|
||||||
|
return altCostSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param card
|
||||||
|
* @param evokeKeyword
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static SpellAbility makeEvokeSpell(final Card card, final String evokeKeyword) {
|
||||||
|
final SpellAbility evokedSpell = new Spell(card) {
|
||||||
|
private static final long serialVersionUID = -1598664196463358630L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve() {
|
||||||
|
card.setEvoked(true);
|
||||||
|
Singletons.getModel().getGame().getAction().moveToPlay(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlayAI() {
|
||||||
|
if (!SpellPermanent.checkETBEffects(card, (AIPlayer) this.getActivatingPlayer())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.canPlayAI();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
card.removeIntrinsicKeyword(evokeKeyword);
|
||||||
|
|
||||||
|
final String[] k = evokeKeyword.split(":");
|
||||||
|
final String evokedCost = k[1];
|
||||||
|
|
||||||
|
evokedSpell.setManaCost(new ManaCost(new ManaCostParser(evokedCost)));
|
||||||
|
|
||||||
|
final StringBuilder desc = new StringBuilder();
|
||||||
|
desc.append("Evoke ").append(evokedCost);
|
||||||
|
desc.append(" (You may cast this spell for its evoke cost. ");
|
||||||
|
desc.append("If you do, when it enters the battlefield, sacrifice it.)");
|
||||||
|
|
||||||
|
evokedSpell.setDescription(desc.toString());
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(card.getName()).append(" (Evoked)");
|
||||||
|
evokedSpell.setStackDescription(sb.toString());
|
||||||
|
evokedSpell.setBasicSpell(false);
|
||||||
|
return evokedSpell;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setupETBReplacementAbility(SpellAbility sa) {
|
public static void setupETBReplacementAbility(SpellAbility sa) {
|
||||||
SpellAbility tailend = sa;
|
SpellAbility tailend = sa;
|
||||||
while (tailend.getSubAbility() != null) {
|
while (tailend.getSubAbility() != null) {
|
||||||
@@ -3574,7 +3593,7 @@ public class CardFactoryUtil {
|
|||||||
public static final int hasKeyword(final Card c, final String k) {
|
public static final int hasKeyword(final Card c, final String k) {
|
||||||
final ArrayList<String> a = c.getKeyword();
|
final ArrayList<String> a = c.getKeyword();
|
||||||
for (int i = 0; i < a.size(); i++) {
|
for (int i = 0; i < a.size(); i++) {
|
||||||
if (a.get(i).toString().startsWith(k)) {
|
if (a.get(i).startsWith(k)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3598,7 +3617,7 @@ public class CardFactoryUtil {
|
|||||||
static final int hasKeyword(final Card c, final String k, final int startPos) {
|
static final int hasKeyword(final Card c, final String k, final int startPos) {
|
||||||
final ArrayList<String> a = c.getKeyword();
|
final ArrayList<String> a = c.getKeyword();
|
||||||
for (int i = startPos; i < a.size(); i++) {
|
for (int i = startPos; i < a.size(); i++) {
|
||||||
if (a.get(i).toString().startsWith(k)) {
|
if (a.get(i).startsWith(k)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class InputPassPriority extends Input {
|
|||||||
final Player player = Singletons.getModel().getGame().getPhaseHandler().getPriorityPlayer();
|
final Player player = Singletons.getModel().getGame().getPhaseHandler().getPriorityPlayer();
|
||||||
|
|
||||||
if (player.isComputer()) {
|
if (player.isComputer()) {
|
||||||
System.out.println(phase + ": Computer in passpriority");
|
System.err.println(phase + ": Computer in passpriority");
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
Reference in New Issue
Block a user