CardFactoryUtil: make MorphUp and Manifest to Static API abilities in SetState (better for AI)

Suspend use new Trigger Format, better use for the temporal Suspend Effects
This commit is contained in:
Hanmac
2016-10-28 12:55:58 +00:00
parent f2b68512b3
commit ce38d10307
4 changed files with 94 additions and 141 deletions

View File

@@ -61,8 +61,6 @@ public class PumpAllEffect extends SpellAbilityEffect {
} }
if (suspend && !tgtC.hasSuspend()) { if (suspend && !tgtC.hasSuspend()) {
tgtC.setSuspend(true); tgtC.setSuspend(true);
CardFactoryUtil.addSuspendUpkeepTrigger(tgtC);
CardFactoryUtil.addSuspendPlayTrigger(tgtC);
} }
if (sa.hasParam("RememberAllPumped")) { if (sa.hasParam("RememberAllPumped")) {

View File

@@ -7,7 +7,6 @@ import forge.game.GameEntity;
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;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardUtil; import forge.game.card.CardUtil;
import forge.game.event.GameEventCardStatsChanged; import forge.game.event.GameEventCardStatsChanged;
import forge.game.player.Player; import forge.game.player.Player;
@@ -45,8 +44,6 @@ public class PumpEffect extends SpellAbilityEffect {
kws.add(kw); kws.add(kw);
if (kw.equals("Suspend") && !applyTo.hasSuspend()) { if (kw.equals("Suspend") && !applyTo.hasSuspend()) {
applyTo.setSuspend(true); applyTo.setSuspend(true);
CardFactoryUtil.addSuspendUpkeepTrigger(applyTo);
CardFactoryUtil.addSuspendPlayTrigger(applyTo);
} }
} }
} }

View File

@@ -1,10 +1,13 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import forge.game.Game; import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.game.event.GameEventCardStatsChanged; import forge.game.event.GameEventCardStatsChanged;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import java.util.Iterator; import java.util.Iterator;
@@ -38,13 +41,16 @@ public class SetStateEffect extends SpellAbilityEffect {
@Override @Override
public void resolve(final SpellAbility sa) { public void resolve(final SpellAbility sa) {
final Player p = sa.getActivatingPlayer();
final String mode = sa.getParam("Mode"); final String mode = sa.getParam("Mode");
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
final Game game = host.getGame(); final Game game = host.getGame();
final List<Card> tgtCards = getTargetCards(sa); final List<Card> tgtCards = getTargetCards(sa);
final boolean remChanged = sa.hasParam("RememberChanged"); final boolean remChanged = sa.hasParam("RememberChanged");
final boolean morphUp = sa.hasParam("MorphUp");
final boolean manifestUp = sa.hasParam("ManifestUp");
final boolean hiddenAgenda = sa.hasParam("HiddenAgenda");
for (final Card tgt : tgtCards) { for (final Card tgt : tgtCards) {
if (sa.usesTargeting() && !tgt.canBeTargetedBy(sa)) { if (sa.usesTargeting() && !tgt.canBeTargetedBy(sa)) {
@@ -67,12 +73,32 @@ public class SetStateEffect extends SpellAbilityEffect {
} }
} }
boolean hasTransformed = tgt.changeCardState(mode, sa.getParam("NewState")); boolean hasTransformed = false;
if ( hasTransformed ) { if (morphUp) {
game.fireEvent(new GameEventCardStatsChanged(tgt)); hasTransformed = tgt.turnFaceUp();
} else if (manifestUp) {
hasTransformed = tgt.turnFaceUp(true, true);
} else {
hasTransformed = tgt.changeCardState(mode, sa.getParam("NewState"));
} }
if ( hasTransformed && remChanged) { if ( hasTransformed ) {
host.addRemembered(tgt); if (morphUp) {
String sb = p + " has unmorphed " + tgt.getName();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
} else if (manifestUp) {
String sb = p + " has unmanifested " + tgt.getName();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
} else if (hiddenAgenda) {
String sb = p + " has revealed " + tgt.getName() + " with the chosen name " + tgt.getNamedCard();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
}
game.fireEvent(new GameEventCardStatsChanged(tgt));
if (sa.hasParam("Mega")) {
tgt.addCounter(CounterType.P1P1, 1, true);
}
if (remChanged) {
host.addRemembered(tgt);
}
} }
} }
} }

View File

@@ -52,8 +52,6 @@ import forge.game.ability.ApiType;
import forge.game.card.Card.SplitCMCMode; import forge.game.card.Card.SplitCMCMode;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostPayment;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordsChange; import forge.game.keyword.KeywordsChange;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
@@ -136,39 +134,21 @@ public class CardFactoryUtil {
* a {@link forge.game.cost.Cost} object. * a {@link forge.game.cost.Cost} object.
* @return a {@link forge.game.spellability.AbilityActivated} object. * @return a {@link forge.game.spellability.AbilityActivated} object.
*/ */
public static AbilityStatic abilityMorphUp(final Card sourceCard, final Cost cost, final boolean mega) { public static SpellAbility abilityMorphUp(final Card sourceCard, final String costStr, final boolean mega) {
final AbilityStatic morphUp = new AbilityStatic(sourceCard, cost, null) { Cost cost = new Cost(costStr, true);
@Override
public void resolve() {
if (sourceCard.turnFaceUp()) {
String sb = this.getActivatingPlayer() + " has unmorphed " + sourceCard.getName();
sourceCard.getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
sourceCard.getGame().fireEvent(new GameEventCardStatsChanged(sourceCard));
}
if (mega) {
sourceCard.addCounter(CounterType.P1P1, 1, true);
}
}
@Override
public boolean canPlay() {
return sourceCard.getController().equals(this.getActivatingPlayer()) && sourceCard.isFaceDown()
&& sourceCard.isInPlay() && CostPayment.canPayAdditionalCosts(cost, this);
}
}; // morph_up
String costDesc = cost.toString(); String costDesc = cost.toString();
// get rid of the ": " at the end // get rid of the ": " at the end
costDesc = costDesc.substring(0, costDesc.length() - 2); costDesc = costDesc.substring(0, costDesc.length() - 2);
final StringBuilder sb = new StringBuilder();
sb.append("Morph"); String ab = "ST$ SetState | Cost$ " + costStr + " | CostDesc$ Morph " + costDesc
if (!cost.isOnlyManaCost()) { + " | MorphUp$ True"
sb.append(" -"); + " | ConditionDefined$ Self | ConditionPresent$ Card.faceDown"
+ " | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its morph cost.)";
if (mega) {
ab += " | Mega$ True";
} }
sb.append(" ").append(costDesc).append(" (Turn this face up any time for its morph cost.)");
morphUp.setDescription(sb.toString()); final SpellAbility morphUp = AbilityFactory.getAbility(ab, sourceCard);
final StringBuilder sbStack = new StringBuilder(); final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up."); sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
@@ -178,38 +158,20 @@ public class CardFactoryUtil {
return morphUp; return morphUp;
} }
public static AbilityStatic abilityManifestFaceUp(final Card sourceCard, final ManaCost manaCost) { public static SpellAbility abilityManifestFaceUp(final Card sourceCard, final ManaCost manaCost) {
final Cost cost = new Cost(manaCost, true); final Cost cost = new Cost(manaCost, true);
final AbilityStatic manifestUp = new AbilityStatic(sourceCard, cost, null) {
@Override
public void resolve() {
if (sourceCard.turnFaceUp(true, true)) {
String sb = this.getActivatingPlayer() + " has unmanifested " + sourceCard.getName();
sourceCard.getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
sourceCard.getGame().fireEvent(new GameEventCardStatsChanged(sourceCard));
}
}
@Override
public boolean canPlay() {
return sourceCard.getController().equals(this.getActivatingPlayer()) && sourceCard.isFaceDown()
&& sourceCard.isInPlay() && sourceCard.isManifested();
}
}; // manifest_up
String costDesc = cost.toString(); String costDesc = cost.toString();
// get rid of the ": " at the end // get rid of the ": " at the end
costDesc = costDesc.substring(0, costDesc.length() - 2); costDesc = costDesc.substring(0, costDesc.length() - 2);
final StringBuilder sb = new StringBuilder();
sb.append("Unmanifest"); String ab = "ST$ SetState | CostDesc$ Unmanifest " + costDesc
if (!cost.isOnlyManaCost()) { + " | ManifestUp$ True"
sb.append(" -"); + " | ConditionDefined$ Self | ConditionPresent$ Card.faceDown+manifested"
} + " | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its mana cost.)";
sb.append(" ").append(costDesc).append(" (Turn this face up any time for its mana cost.)");
manifestUp.setDescription(sb.toString()); final SpellAbility manifestUp = AbilityFactory.getAbility(ab, sourceCard);
manifestUp.setPayCosts(cost);
final StringBuilder sbStack = new StringBuilder(); final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up."); sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
@@ -237,30 +199,12 @@ public class CardFactoryUtil {
return true; return true;
} }
public static AbilityStatic abilityRevealHiddenAgenda(final Card sourceCard) { private static SpellAbility abilityRevealHiddenAgenda(final Card sourceCard) {
final AbilityStatic revealAgenda = new AbilityStatic(sourceCard, Cost.Zero, null) { String ab = "ST$ SetState"
+ " | ConditionDefined$ Self | ConditionPresent$ Card.faceDown+inZoneCommand"
@Override + " | HiddenAgenda$ True"
public void resolve() { + " | Mode$ TurnFace | SpellDescription$ Reveal this Hidden Agenda at any time.";
if (sourceCard.turnFaceUp()) { return AbilityFactory.getAbility(ab, sourceCard);
String sb = this.getActivatingPlayer() + " has revealed " + sourceCard.getName() + " with the chosen name " + sourceCard.getNamedCard();
sourceCard.getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
sourceCard.getGame().fireEvent(new GameEventCardStatsChanged(sourceCard));
}
}
@Override
public boolean canPlay() {
return sourceCard.getController().equals(this.getActivatingPlayer()) && sourceCard.isFaceDown()
&& sourceCard.isInZone(ZoneType.Command);
}
// TODO When should the AI activate this?
}; // reveal hidden agenda
revealAgenda.setDescription("Reveal this Hidden Agenda at any time. ");
return revealAgenda;
} }
/** /**
@@ -321,47 +265,6 @@ public class CardFactoryUtil {
return suspend; return suspend;
} // abilitySuspendStatic() } // abilitySuspendStatic()
public static void addSuspendUpkeepTrigger(Card card) {
//upkeep trigger
StringBuilder upkeepTrig = new StringBuilder();
String triggerSvar = "SuspendTrigSV"; // FIXME: the SVars were previously random UUIDs (which caused the keyword not to work).
String removeCounterSvar = "SuspendRemoveCtrSV";
upkeepTrig.append("Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Exile | CheckSVar$ ");
upkeepTrig.append(triggerSvar);
upkeepTrig.append(" | SVarCompare$ GE1 | References$ ");
upkeepTrig.append(triggerSvar);
upkeepTrig.append(" | Execute$ ");
upkeepTrig.append(removeCounterSvar);
// Mark this trigger as Secondary, so it's not displayed twice
upkeepTrig.append(" | Secondary$ True | TriggerDescription$ At the beginning of your upkeep, if this card is suspended, remove a time counter from it");
card.setSVar(removeCounterSvar, "DB$ RemoveCounter | Defined$ Self | CounterType$ TIME | CounterNum$ 1");
card.setSVar(triggerSvar,"Count$ValidExile Card.Self+suspended");
final Trigger parsedUpkeepTrig = TriggerHandler.parseTrigger(upkeepTrig.toString(), card, true);
card.addTrigger(parsedUpkeepTrig);
}
public static void addSuspendPlayTrigger(Card card) {
//play trigger
StringBuilder playTrig = new StringBuilder();
String playSvar = "SuspendPlaySV"; // FIXME: the SVars were previously random UUIDs (which caused the keyword not to work).
playTrig.append("Mode$ CounterRemoved | TriggerZones$ Exile | ValidCard$ Card.Self | CounterType$ TIME | NewCounterAmount$ 0 | Secondary$ True | Execute$ ");
playTrig.append(playSvar);
playTrig.append(" | TriggerDescription$ When the last time counter is removed from this card, if it's exiled, play it without paying its mana cost if able. ");
playTrig.append("If you can't, it remains exiled. If you cast a creature spell this way, it gains haste until you lose control of the spell or the permanent it becomes.");
StringBuilder playWithoutCost = new StringBuilder();
playWithoutCost.append("DB$ Play | Defined$ Self | WithoutManaCost$ True | SuspendCast$ True");
final Trigger parsedPlayTrigger = TriggerHandler.parseTrigger(playTrig.toString(), card, true);
card.addTrigger(parsedPlayTrigger);
card.setSVar(playSvar, playWithoutCost.toString());
}
/** /**
* <p> * <p>
* multiplyCost. * multiplyCost.
@@ -2304,15 +2207,14 @@ public class CardFactoryUtil {
card.addTrigger(parsedUpkeepTrig); card.addTrigger(parsedUpkeepTrig);
} }
else if (keyword.startsWith("Suspend")) { else if (keyword.startsWith("Suspend")) {
card.removeIntrinsicKeyword(keyword);
card.setSuspend(true); card.setSuspend(true);
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final String timeCounters = k[1]; final String timeCounters = k[1];
final String cost = k[2]; final String cost = k[2];
card.addSpellAbility(abilitySuspendStatic(card, cost, timeCounters)); card.addSpellAbility(abilitySuspendStatic(card, cost, timeCounters));
addSuspendUpkeepTrigger(card);
addSuspendPlayTrigger(card); addTriggerAbility(keyword, card, null);
} }
else if (keyword.startsWith("Fading")) { else if (keyword.startsWith("Fading")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
@@ -3162,6 +3064,38 @@ public class CardFactoryUtil {
if (!intrinsic) { if (!intrinsic) {
kws.addTrigger(cardTrigger); kws.addTrigger(cardTrigger);
} }
} else if (keyword.startsWith("Suspend")) {
//upkeep trigger
StringBuilder upkeepTrig = new StringBuilder();;
upkeepTrig.append("Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Exile ");
upkeepTrig.append(" | IsPresent$ Card.Self+suspended | PresentZone$ Exile");
// Mark this trigger as Secondary, so it's not displayed twice
upkeepTrig.append(" | Secondary$ True | TriggerDescription$ At the beginning of your upkeep, if this card is suspended, remove a time counter from it");
final String abRemove = "DB$ RemoveCounter | Defined$ Self | CounterType$ TIME | CounterNum$ 1";
final Trigger parsedUpkeepTrig = TriggerHandler.parseTrigger(upkeepTrig.toString(), card, intrinsic);
parsedUpkeepTrig.setOverridingAbility(AbilityFactory.getAbility(abRemove, card));
final Trigger cardTriggerUpkeep = card.addTrigger(parsedUpkeepTrig);
//play trigger
StringBuilder playTrig = new StringBuilder();
playTrig.append("Mode$ CounterRemoved | TriggerZones$ Exile | ValidCard$ Card.Self | CounterType$ TIME | NewCounterAmount$ 0 | Secondary$ True ");
playTrig.append(" | TriggerDescription$ When the last time counter is removed from this card, if it's exiled, play it without paying its mana cost if able. ");
playTrig.append("If you can't, it remains exiled. If you cast a creature spell this way, it gains haste until you lose control of the spell or the permanent it becomes.");
final String abPlay = "DB$ Play | Defined$ Self | WithoutManaCost$ True | SuspendCast$ True";
final Trigger parsedPlayTrigger = TriggerHandler.parseTrigger(playTrig.toString(), card, intrinsic);
parsedPlayTrigger.setOverridingAbility(AbilityFactory.getAbility(abPlay, card));
final Trigger cardTriggerPlay = card.addTrigger(parsedPlayTrigger);
if (!intrinsic) {
kws.addTrigger(cardTriggerUpkeep);
kws.addTrigger(cardTriggerPlay);
}
} else if (keyword.equals("Undying")) { } else if (keyword.equals("Undying")) {
final String trigStr = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | OncePerEffect$ True " + final String trigStr = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | OncePerEffect$ True " +
" | Execute$ UndyingReturn | ValidCard$ Card.Self+counters_EQ0_P1P1 | Secondary$ True" + " | Execute$ UndyingReturn | ValidCard$ Card.Self+counters_EQ0_P1P1 | Secondary$ True" +
@@ -3995,13 +3929,12 @@ public class CardFactoryUtil {
Map<String, String> sVars = card.getSVars(); Map<String, String> sVars = card.getSVars();
final String[] k = parse.split(":"); final String[] k = parse.split(":");
final Cost cost = new Cost(k[1], true);
card.addSpellAbility(abilityMorphDown(card)); card.addSpellAbility(abilityMorphDown(card));
card.setState(CardStateName.FaceDown, false); card.setState(CardStateName.FaceDown, false);
card.addSpellAbility(abilityMorphUp(card, cost, false)); card.addSpellAbility(abilityMorphUp(card, k[1], false));
card.setSVars(sVars); // for Warbreak Trumpeter. card.setSVars(sVars); // for Warbreak Trumpeter.
card.setState(CardStateName.Original, false); card.setState(CardStateName.Original, false);
@@ -4016,13 +3949,12 @@ public class CardFactoryUtil {
Map<String, String> sVars = card.getSVars(); Map<String, String> sVars = card.getSVars();
final String[] k = parse.split(":"); final String[] k = parse.split(":");
final Cost cost = new Cost(k[1], true);
card.addSpellAbility(abilityMorphDown(card)); card.addSpellAbility(abilityMorphDown(card));
card.setState(CardStateName.FaceDown, false); card.setState(CardStateName.FaceDown, false);
card.addSpellAbility(abilityMorphUp(card, cost, true)); card.addSpellAbility(abilityMorphUp(card, k[1], true));
card.setSVars(sVars); card.setSVars(sVars);
card.setState(CardStateName.Original, false); card.setState(CardStateName.Original, false);
} }