diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java index a7c850abef4..f2c37658ead 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java @@ -206,8 +206,9 @@ public class AnimateEffect extends AnimateEffectBase { protected String getStackDescription(SpellAbility sa) { final Card host = sa.getHostCard(); final StringBuilder sb = new StringBuilder(); - final List tgts = getCardsfromTargets(sa); - final boolean justOne = tgts.size() == 1; + final List tgts = getDefinedCardsOrTargeted(sa); + //possible to be building stack desc before Defined is populated... for now, 0 will default to singular + final boolean justOne = tgts.size() <= 1; if (sa.hasParam("IfDesc")) { if (sa.getParam("IfDesc").equals("True") && sa.hasParam("SpellDescription")) { @@ -264,7 +265,7 @@ public class AnimateEffect extends AnimateEffectBase { } else { sb.append("toughness ").append(toughness).append(" "); } - } else { + } else if (sb.length() > initial) { sb.append(justOne ? "becomes " : "become "); } @@ -291,7 +292,8 @@ public class AnimateEffect extends AnimateEffectBase { } } if (keywords.size() > 0) { - sb.append("and gains ").append(Lang.joinHomogenous(keywords).toLowerCase()).append(" "); + sb.append(sb.length() > initial ? "and " : "").append(" gains "); + sb.append(Lang.joinHomogenous(keywords).toLowerCase()).append(" "); } // sb.append(abilities) // sb.append(triggers) @@ -317,6 +319,20 @@ public class AnimateEffect extends AnimateEffectBase { } sb.append("."); + if (sa.hasParam("AtEOT")) { + sb.append(" "); + final String eot = sa.getParam("AtEOT"); + final String pronoun = justOne ? "it" : "them"; + if (eot.equals("Hand")) { + sb.append("Return ").append(pronoun).append(" to your hand"); + } else if (eot.equals("SacrificeCtrl")) { + sb.append(justOne ? "Its controller sacrifices it" : "Their controllers sacrifice them"); + } else { //Sacrifice,Exile + sb.append(eot).append(" ").append(pronoun); + } + sb.append(" at the beginning of the next end step."); + } + return sb.toString(); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 0319d92273c..f3bb1b43b63 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -293,15 +293,36 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // TODO Expand on this Description as more cards use it // for the non-targeted SAs when you choose what is returned on resolution sb.append("Return ").append(num).append(" ").append(type).append(" card(s) "); - sb.append(" to your ").append(destination); + sb.append(" to your ").append(destination).append("."); } else if (origin.equals("Graveyard")) { // for non-targeted SAs when you choose what is moved on resolution // this will need expansion as more cards use it - sb.append(chooserNames).append(" puts "); final String cardTag = type.contains("card") ? "" : " card"; - sb.append(num != 0 ? Lang.nounWithNumeralExceptOne(num, type + cardTag) : - sa.getParamOrDefault("ChangeNumDesc", "") + " " + type + cardTag); - sb.append(" into their ").append(destination.toLowerCase()).append("."); + final boolean changeNumDesc = sa.hasParam("ChangeNumDesc"); + final boolean mandatory = sa.hasParam("Mandatory"); + String changed; + if (changeNumDesc) { + changed = sa.getParam("ChangeNumDesc") + " " + type + cardTag; + } else if (!mandatory) { + changed = Lang.nounWithNumeral(num, type + cardTag); + } else { + changed = Lang.nounWithNumeralExceptOne(num, type + cardTag); + } + final boolean battlefield = destination.equals("Battlefield"); + sb.append(chooserNames).append(" returns ").append(mandatory || changeNumDesc ? "" : "up to "); + sb.append(changed); + // so far, it seems non-targeted only lets you return from your own graveyard + sb.append(" from their graveyard").append(choosers.size() > 1 ? "s" : ""); + sb.append(battlefield ? " to the " : " into their ").append(destination.toLowerCase()); + if (sa.hasParam("WithCountersType")) { + final CounterType cType = CounterType.getType(sa.getParam("WithCountersType")); + if (cType != null) { + sb.append(" with an additional ").append(cType.getName()).append(" counter on it"); + } else { + sb.append(" [ChangeZoneEffect WithCountersType error]"); + } + } + sb.append("."); } return sb.toString(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java index 2c85562b1ed..eb422e732d7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java @@ -11,6 +11,7 @@ import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardZoneTable; import forge.game.player.Player; +import forge.game.player.PlayerCollection; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.Lang; @@ -92,17 +93,40 @@ public class MillEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); final int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) : 1; + final boolean optional = sa.hasParam("Optional"); + final boolean eachP = sa.hasParam("Defined") && sa.getParam("Defined").equals("Player"); + String each = "Each player"; + final PlayerCollection tgtPs = getTargetPlayers(sa); - sb.append(Lang.joinHomogenous(getTargetPlayers(sa))).append(" "); + if (sa.hasParam("IfDesc")) { + final String ifD = sa.getParam("IfDesc"); + if (ifD.equals("True")) { + String ifDesc = sa.getDescription(); + if (ifDesc.contains(",")) { + sb.append(ifDesc, 0, ifDesc.indexOf(",") + 1); + } else { + sb.append("[MillEffect IfDesc parsing error]"); + } + } else { + sb.append(ifD); + } + sb.append(" "); + each = each.toLowerCase(); + } + + sb.append(eachP ? each : Lang.joinHomogenous(tgtPs)); + sb.append(" "); final ZoneType dest = ZoneType.smartValueOf(sa.getParam("Destination")); + sb.append(optional ? "may " : ""); if ((dest == null) || dest.equals(ZoneType.Graveyard)) { - sb.append("mills "); + sb.append("mill"); } else if (dest.equals(ZoneType.Exile)) { - sb.append("exiles "); + sb.append("exile"); } else if (dest.equals(ZoneType.Ante)) { - sb.append("antes "); + sb.append("ante"); } + sb.append((optional || tgtPs.size() > 1) && !eachP ? " " : "s "); sb.append(Lang.nounWithNumeralExceptOne(numCards, "card")).append("."); diff --git a/forge-gui/res/cardsfolder/b/blood_for_bones.txt b/forge-gui/res/cardsfolder/b/blood_for_bones.txt index 3e1f85e6931..949a151a314 100644 --- a/forge-gui/res/cardsfolder/b/blood_for_bones.txt +++ b/forge-gui/res/cardsfolder/b/blood_for_bones.txt @@ -1,8 +1,8 @@ Name:Blood for Bones ManaCost:3 B Types:Sorcery -A:SP$ ChangeZone | Cost$ 3 B Sac<1/Creature> | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn | RememberChanged$ True | SubAbility$ DBChangeZone | SpellDescription$ Return a creature card from your graveyard to the battlefield, then return another creature card from your graveyard to your hand. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn+IsNotRemembered | SubAbility$ DBCleanup +A:SP$ ChangeZone | Cost$ 3 B Sac<1/Creature> | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn | PrimaryPrompt$ Choose a creature card to return to the battlefield | ChangeTypeDesc$ creature | RememberChanged$ True | SubAbility$ DBChangeZone | SpellDescription$ Return a creature card from your graveyard to the battlefield, then return another creature card from your graveyard to your hand. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn+IsNotRemembered | PrimaryPrompt$ Choose another creature card to return to your hand | ChangeNumDesc$ another | ChangeTypeDesc$ creature | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:Random Oracle:As an additional cost to cast this spell, sacrifice a creature.\nReturn a creature card from your graveyard to the battlefield, then return another creature card from your graveyard to your hand. diff --git a/forge-gui/res/cardsfolder/b/blood_on_the_snow.txt b/forge-gui/res/cardsfolder/b/blood_on_the_snow.txt index 6b023dd1ba2..b8947221f3e 100644 --- a/forge-gui/res/cardsfolder/b/blood_on_the_snow.txt +++ b/forge-gui/res/cardsfolder/b/blood_on_the_snow.txt @@ -4,7 +4,7 @@ Types:Snow Sorcery A:SP$ Charm | Cost$ 4 B B | Choices$ DestroyCtrs,DestroyPWs | CharmNum$ 1 | SpellDescription$ Then return a creature or planeswalker card with mana value X or less from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell. ({S} is mana from a snow source.) SVar:DestroyCtrs:DB$ DestroyAll | ValidCards$ Creature | SubAbility$ DBReturn | SpellDescription$ Destroy all creatures. SVar:DestroyPWs:DB$ DestroyAll | ValidCards$ Planeswalker | SubAbility$ DBReturn | SpellDescription$ Destroy all planeswalkers. -SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn+cmcLEX,Planeswalker.YouOwn+cmcLEX | SpellDescription$ Then return a creature or planeswalker card with mana value X or less from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell. ({S} is mana from a snow source.) +SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn+cmcLEX,Planeswalker.YouOwn+cmcLEX | ChangeTypeDesc$ creature or planeswalker card with mana value X or less | SpellDescription$ Then return a creature or planeswalker card with mana value X or less from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell. ({S} is mana from a snow source.) SVar:X:Count$CastTotalManaSpent Snow SVar:AIPreference:ManaFrom$Snow AI:RemoveDeck:Random diff --git a/forge-gui/res/cardsfolder/b/bond_of_insight.txt b/forge-gui/res/cardsfolder/b/bond_of_insight.txt index 9a61fa4e388..ac7b57105af 100644 --- a/forge-gui/res/cardsfolder/b/bond_of_insight.txt +++ b/forge-gui/res/cardsfolder/b/bond_of_insight.txt @@ -1,8 +1,8 @@ Name:Bond of Insight ManaCost:3 U Types:Sorcery -A:SP$ Mill | Cost$ 3 U | NumCards$ 4 | Defined$ Player | SubAbility$ DBChangeZone | StackDescription$ Each player mills four cards. | SpellDescription$ Each player mills four cards. Return up to two instant and/or sorcery cards from your graveyard to your hand. Exile CARDNAME. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ChangeNum$ 2 | ChangeType$ Instant.YouOwn,Sorcery.YouOwn | SelectPrompt$ Select up to two instant and/or sorcery cards from your graveyard to return to your hand | Hidden$ True | SubAbility$ DBExile | StackDescription$ {p:You} returns up to two instant and/or sorcery cards from their graveyard to their hand. +A:SP$ Mill | NumCards$ 4 | Defined$ Player | SubAbility$ DBChangeZone | SpellDescription$ Each player mills four cards. Return up to two instant and/or sorcery cards from your graveyard to your hand. Exile CARDNAME. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ChangeNum$ 2 | ChangeType$ Instant.YouOwn,Sorcery.YouOwn | ChangeTypeDesc$ instant and/or sorcery | SelectPrompt$ Select up to two instant and/or sorcery cards from your graveyard to return to your hand | Hidden$ True | SubAbility$ DBExile | StackDescription$ {p:You} returns up to two instant and/or sorcery cards from their graveyard to their hand. SVar:DBExile:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ Exile CARDNAME. DeckHas:Ability$Graveyard|Mill DeckHints:Type$Instant|Sorcery diff --git a/forge-gui/res/cardsfolder/c/cauldrons_gift.txt b/forge-gui/res/cardsfolder/c/cauldrons_gift.txt index 05dd7e54368..587d4c90daf 100644 --- a/forge-gui/res/cardsfolder/c/cauldrons_gift.txt +++ b/forge-gui/res/cardsfolder/c/cauldrons_gift.txt @@ -1,8 +1,8 @@ Name:Cauldron's Gift ManaCost:4 B Types:Sorcery -A:SP$ Mill | Cost$ 4 B | NumCards$ 4 | Defined$ You | SubAbility$ DBChangeZone | ConditionCheckSVar$ X | AIManaPref$ B | SpellDescription$ Adamant — If at least three black mana was spent to cast this spell, mill four cards. You may choose a creature card in your graveyard. If you do, return it to the battlefield with an additional +1/+1 counter on it. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouOwn | Hidden$ True | ChangeNum$ 1 | WithCountersType$ P1P1 +A:SP$ Mill | NumCards$ 4 | SubAbility$ DBChangeZone | ConditionCheckSVar$ X | AIManaPref$ B | IfDesc$ True | SpellDescription$ Adamant — If at least three black mana was spent to cast this spell, mill four cards. You may choose a creature card in your graveyard. If you do, return it to the battlefield with an additional +1/+1 counter on it. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouOwn | ChangeTypeDesc$ creature | Hidden$ True | ChangeNum$ 1 | WithCountersType$ P1P1 SVar:X:Count$Adamant.Black.1.0 -DeckHas:Ability$Counters|Graveyard +DeckHas:Ability$Counters|Graveyard|Mill Oracle:Adamant — If at least three black mana was spent to cast this spell, mill four cards.\nYou may choose a creature card in your graveyard. If you do, return it to the battlefield with an additional +1/+1 counter on it. diff --git a/forge-gui/res/cardsfolder/c/connive_concoct.txt b/forge-gui/res/cardsfolder/c/connive_concoct.txt index ea2876ea01d..21e7060f134 100644 --- a/forge-gui/res/cardsfolder/c/connive_concoct.txt +++ b/forge-gui/res/cardsfolder/c/connive_concoct.txt @@ -1,7 +1,7 @@ Name:Connive ManaCost:2 U/B U/B Types:Sorcery -A:SP$ GainControl | Cost$ 2 U/B U/B | ValidTgts$ Creature.powerLE2 | TgtPrompt$ Select target creature with power 2 or less. | SpellDescription$ Gain control of target creature with power 2 or less. +A:SP$ GainControl | ValidTgts$ Creature.powerLE2 | TgtPrompt$ Select target creature with power 2 or less | SpellDescription$ Gain control of target creature with power 2 or less. AlternateMode:Split Oracle:Gain control of target creature with power 2 or less. @@ -10,7 +10,7 @@ ALTERNATE Name:Concoct ManaCost:3 U B Types:Sorcery -A:SP$ Surveil | Cost$ 3 U B | Amount$ 3 | SubAbility$ DBReturn | SpellDescription$ Surveil 3, then return a creature card from your graveyard to the battlefield. -SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouCtrl | ChangeNum$ 1 | Hidden$ True +A:SP$ Surveil | Amount$ 3 | SubAbility$ DBReturn | SpellDescription$ Surveil 3, then return a creature card from your graveyard to the battlefield. +SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouOwn | ChangeTypeDesc$ creature | ChangeNum$ 1 | Hidden$ True | Mandatory$ True DeckHas:Ability$Surveil|Graveyard Oracle:Surveil 3, then return a creature card from your graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/c/corpse_dance.txt b/forge-gui/res/cardsfolder/c/corpse_dance.txt index 048dc7ae239..a35f7161362 100644 --- a/forge-gui/res/cardsfolder/c/corpse_dance.txt +++ b/forge-gui/res/cardsfolder/c/corpse_dance.txt @@ -2,8 +2,9 @@ Name:Corpse Dance ManaCost:2 B Types:Instant K:Buyback:2 -A:SP$ ChangeZone | Cost$ 2 B | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.TopGraveyardCreature+YouCtrl | Hidden$ True | Mandatory$ True | RememberChanged$ True | SubAbility$ DBPump | SpellDescription$ Return the top creature card of your graveyard to the battlefield. That creature gains haste until end of turn. Exile it at the beginning of the next end step. -SVar:DBPump:DB$ Animate | Keywords$ Haste | Defined$ Remembered | AtEOT$ Exile | SubAbility$ DBCleanup +A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.TopGraveyardCreature+YouOwn | Hidden$ True | Mandatory$ True | RememberChanged$ True | SubAbility$ DBPump | StackDescription$ {p:You} returns the top creature card of their graveyard to the battlefield. | SpellDescription$ Return the top creature card of your graveyard to the battlefield. +SVar:DBPump:DB$ Animate | Keywords$ Haste | Defined$ Remembered | AtEOT$ Exile | SubAbility$ DBCleanup | DefinedDesc$ That creature | SpellDescription$ That creature gains haste until end of turn. Exile it at the beginning of the next end step. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:NeedsOrderedGraveyard:TRUE +DeckHas:Ability$Graveyard Oracle:Buyback {2} (You may pay an additional {2} as you cast this spell. If you do, put this card into your hand as it resolves.)\nReturn the top creature card of your graveyard to the battlefield. That creature gains haste until end of turn. Exile it at the beginning of the next end step. diff --git a/forge-gui/res/cardsfolder/e/eerie_ultimatum.txt b/forge-gui/res/cardsfolder/e/eerie_ultimatum.txt index ecda79df8f6..355ff616eff 100644 --- a/forge-gui/res/cardsfolder/e/eerie_ultimatum.txt +++ b/forge-gui/res/cardsfolder/e/eerie_ultimatum.txt @@ -1,7 +1,8 @@ Name:Eerie Ultimatum ManaCost:W W B B B G G Types:Sorcery -A:SP$ ChangeZone | Cost$ W W B B B G G | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Permanent.YouOwn | DifferentNames$ True | ChangeNum$ X | Hidden$ True | StackDescription$ SpellDescription | SpellDescription$ Return any number of permanent cards with different names from your graveyard to the battlefield. +A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Permanent.YouOwn | ChangeNumDesc$ any number of | ChangeTypeDesc$ permanent cards with different names | DifferentNames$ True | ChangeNum$ X | Hidden$ True | SpellDescription$ Return any number of permanent cards with different names from your graveyard to the battlefield. SVar:X:Count$DifferentCardNames_Permanent.YouOwn+inZoneGraveyard SVar:IsReanimatorCard:TRUE +DeckHas:Ability$Graveyard Oracle:Return any number of permanent cards with different names from your graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/f/forbidden_friendship.txt b/forge-gui/res/cardsfolder/f/forbidden_friendship.txt index 61a0d5d54c3..d57f9db6c64 100644 --- a/forge-gui/res/cardsfolder/f/forbidden_friendship.txt +++ b/forge-gui/res/cardsfolder/f/forbidden_friendship.txt @@ -1,6 +1,6 @@ Name:Forbidden Friendship ManaCost:1 R Types:Sorcery -A:SP$ Token | Cost$ 1 R | TokenAmount$ 1 | TokenScript$ r_1_1_dinosaur_haste,w_1_1_human_soldier | TokenOwner$ You | SpellDescription$ Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token. -DeckHas:Ability$Token +A:SP$ Token | TokenScript$ r_1_1_dinosaur_haste,w_1_1_human_soldier | SpellDescription$ Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token. +DeckHas:Ability$Token & Type$Dinosaur|Human|Soldier Oracle:Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.