MOM: Backup keyword and cards (#2799)

* Backup keyword setup

* Backup cards

* KeywordWithAmount spacing

* mirror_shield_hoplite.txt and support

* fix for KeywordWithAmount

* doomskar_warrior.txt fearless_skald.txt

* add PlayMain1

* typo fix

* add logic to ComputerUtil.castPermanentInMain1 instead of using PlayMain1 on cardscripts
This commit is contained in:
Northmoc
2023-04-06 22:32:41 -04:00
committed by GitHub
parent 2d52a878c1
commit ee418ded60
15 changed files with 141 additions and 6 deletions

View File

@@ -1074,6 +1074,15 @@ public class ComputerUtil {
}
}
// cast Backup creatures in main 1 to pump attackers
if (cardState.hasKeyword(Keyword.BACKUP)) {
for (Card potentialAtkr: ai.getCreaturesInPlay()) {
if (ComputerUtilCard.doesCreatureAttackAI(ai, potentialAtkr)) {
return true;
}
}
}
// try not to cast Raid creatures in main 1 if an attack is likely
if ("Count$AttackersDeclared".equals(card.getSVar("RaidTest")) && !cardState.hasKeyword(Keyword.HASTE)) {
for (Card potentialAtkr: ai.getCreaturesInPlay()) {

View File

@@ -193,6 +193,8 @@ public class ForgeScript {
} else if (property.equals("hasTapCost")) {
Cost cost = sa.getPayCosts();
return cost != null && cost.hasTapCost();
} else if (property.equals("Backup")) {
return sa.isBackup();
} else if (property.equals("Blitz")) {
return sa.isBlitz();
} else if (property.equals("Buyback")) {

View File

@@ -2303,10 +2303,18 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|| keyword.equals("For Mirrodin")) {
// keyword parsing takes care of adding a proper description
} else if(keyword.startsWith("Read ahead")) {
} else if (keyword.startsWith("Read ahead")) {
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
sb.append(" ").append(Localizer.getInstance().getMessage("lblSagaFooter")).append(" ").append(TextUtil.toRoman(getFinalChapterNr())).append(".");
sb.append(")").append("\r\n\r\n");
} else if (keyword.startsWith("Backup")) {
final String[] k = keyword.split(":");
sb.append(k[0]).append(" ").append(k[1]).append(" (");
String remStr = inst.getReminderText();
if (k[2].endsWith("s")) {
remStr = remStr.replace("ability", "abilities");
}
sb.append(remStr).append(")");
} else if (keyword.startsWith("MayEffectFromOpening")) {
final String[] k = keyword.split(":");
// need to get SpellDescription from Svar
@@ -2325,7 +2333,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
sbLong.append("\r\n");
}
if (keyword.equals("Flash")) {
if (keyword.equals("Flash") || keyword.startsWith("Backup")) {
sb.append("\r\n\r\n");
i = 0;
} else {

View File

@@ -839,6 +839,31 @@ public class CardFactoryUtil {
sa.setBlessing(true);
}
}
} else if (keyword.startsWith("Backup")) {
final String[] k = keyword.split(":");
final String magnitude = k[1];
final String backupVar = card.getSVar(k[2]);
String descStr = "Backup " + magnitude;
final String trigStr = "Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | " +
"Secondary$ True | TriggerDescription$ " + descStr;
final String putCounter = "DB$ PutCounter | ValidTgts$ Creature | CounterNum$ " + magnitude
+ " | CounterType$ P1P1 | Backup$ True";
final String addAbility = backupVar + " | ConditionDefined$ Targeted | " +
"ConditionPresent$ Card.Other | Defined$ Targeted";
SpellAbility sa = AbilityFactory.getAbility(putCounter, card);
AbilitySub backupSub = (AbilitySub) AbilityFactory.getAbility(addAbility, card);
sa.setSubAbility(backupSub);
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
sa.setIntrinsic(intrinsic);
trigger.setOverridingAbility(sa);
inst.addTrigger(trigger);
} else if (keyword.equals("Battle cry")) {
final String trig = "Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Secondary$ True "
+ " | TriggerDescription$ " + keyword + " (" + inst.getReminderText() + ")";
@@ -1151,7 +1176,7 @@ public class CardFactoryUtil {
} else if (keyword.equals("Extort")) {
final String extortTrigger = "Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | "
+ "TriggerZones$ Battlefield | Secondary$ True"
+ " | TriggerDescription$ Extort ("+ inst.getReminderText() +")";
+ " | TriggerDescription$ Extort (" + inst.getReminderText() + ")";
final String loseLifeStr = "AB$ LoseLife | Cost$ WB | Defined$ Player.Opponent | LifeAmount$ 1";
final String gainLifeStr = "DB$ GainLife | Defined$ You | LifeAmount$ AFLifeLost";
@@ -2687,7 +2712,7 @@ public class CardFactoryUtil {
if (keyword.startsWith("Alternative Cost") && !host.isLand()) {
final String[] kw = keyword.split(":");
String costStr = kw[1];
for (SpellAbility sa: host.getBasicSpells()) {
for (SpellAbility sa : host.getBasicSpells()) {
if (costStr.equals("ConvertedManaCost")) {
costStr = Integer.toString(host.getCMC());
}

View File

@@ -28,6 +28,7 @@ public enum Keyword {
ASSIST("Assist", SimpleKeyword.class, true, "Another player can pay up to %s of this spell's cost."),
AURA_SWAP("Aura swap", KeywordWithCost.class, false, "%s: You may exchange this Aura with an Aura card in your hand."),
AWAKEN("Awaken", KeywordWithCostAndAmount.class, false, "If you cast this spell for %s, also put {%d:+1/+1 counter} on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land."),
BACKUP("Backup", KeywordWithAmount.class, false, "When this creature enters the battlefield, put {%1$d:+1/+1 counter} on target creature. If that's another creature, it gains the following ability until end of turn."),
BANDING("Banding", SimpleKeyword.class, true, "Any creatures with banding, and up to one without, can attack in a band. Bands are blocked as a group. If any creatures with banding you control are blocking or being blocked by a creature, you divide that creature's combat damage, not its controller, among any of the creatures it's being blocked by or is blocking."),
BATTLE_CRY("Battle cry", SimpleKeyword.class, false, "Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn."),
BESTOW("Bestow", KeywordWithCost.class, false, "If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature."),

View File

@@ -18,7 +18,7 @@ public class KeywordWithAmount extends KeywordInstance<KeywordWithAmount> {
extra = details.split(":")[1];
}
} else {
amount = Integer.parseInt(details);
amount = details.contains(":") ? Integer.parseInt(details.split(":")[0]) : Integer.parseInt(details);
}
}

View File

@@ -508,6 +508,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return isAlternativeCost(AlternativeCost.Cycling);
}
public boolean isBackup() {
return this.hasParam("Backup");
}
public boolean isBoast() {
return this.hasParam("Boast");
}

View File

@@ -0,0 +1,13 @@
Name:Archpriest of Shadows
ManaCost:3 B B
Types:Creature Human Warlock
PT:4/4
K:Backup:1:BackupAbilities
SVar:BackupAbilities:DB$ Animate | Keywords$ Deathtouch | Triggers$ DamageTrig
SVar:DamageTrig:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player,Battle | Execute$ TrigReturn | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to a player or battle, return target creature card from your graveyard to the battlefield.
K:Deathtouch
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player,Battle | Execute$ TrigReturn | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to a player or battle, return target creature card from your graveyard to the battlefield.
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select target creature from your graveyard | Origin$ Graveyard | Destination$ Battlefield
DeckHas:Ability$Counters|Graveyard
DeckHints:Ability$Discard|Sacrifice
Oracle:Backup 1 (When this creature enters the battlefield, put a +1/+1 counter on target creature. If that's another creature, it gains the following abilities until end of turn.)\nDeathtouch\nWhenever this creature deals combat damage to a player or battle, return target creature card from your graveyard to the battlefield.

View File

@@ -0,0 +1,11 @@
Name:Boon-Bringer Valkyrie
ManaCost:3 W W
Types:Creature Angel Warrior
PT:4/4
K:Backup:1:BackupAbilities
SVar:BackupAbilities:DB$ Pump | KW$ Flying & First Strike & Lifelink
K:Flying
K:First Strike
K:Lifelink
DeckHas:Ability$Counters|LifeGain
Oracle:Backup 1 (When this creature enters the battlefield, put a +1/+1 counter on target creature. If thats another creature, it gains the following abilities until end of turn.)\nFlying, first strike, lifelink

View File

@@ -0,0 +1,10 @@
Name:Cragsmasher Yeti
ManaCost:4 R R
Types:Creature Yeti
PT:4/2
K:TypeCycling:Mountain:2
K:Backup:2:BackupAbility
SVar:BackupAbility:DB$ Pump | KW$ Trample
K:Trample
DeckHas:Ability$Counters
Oracle:Mountaincycling {2} ({2}, Discard this card: Search your library for a Mountain card, reveal it, put it into your hand, then shuffle.)\nBackup 2 (When this creature enters the battlefield, put two +1/+1 counters on target creature. If that's another creature, it gains the following ability until end of turn.)\nTrample

View File

@@ -0,0 +1,13 @@
Name:Doomskar Warrior
ManaCost:2 G G
Types:Creature Human Warrior
PT:4/3
K:Backup:1:BackupAbilities
SVar:BackupAbilities:DB$ Animate | Keywords$ Trample | Triggers$ DamageTrig
SVar:DamageTrig:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player,Battle | Execute$ TrigDig | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to a player or battle, look at that many cards from the top of your library. You may reveal a creature or land card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
SVar:TrigDig:DB$ Dig | DigNum$ X | ChangeNum$ 1 | ChangeValid$ Creature,Land | Optional$ True | DestinationZone$ Hand | RestRandomOrder$ True
K:Trample
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player,Planeswalker | Execute$ TrigDig | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to a player or battle, look at that many cards from the top of your library. You may reveal a creature or land card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
SVar:X:TriggerCount$DamageAmount
DeckHas:Ability$Counters
Oracle:Backup 1\nTrample\nWhenever this creature deals combat damage to a player or battle, look at that many cards from the top of your library. You may reveal a creature or land card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.

View File

@@ -0,0 +1,9 @@
Name:Fearless Skald
ManaCost:4 R
Types:Creature Dwarf Berserker
PT:3/2
K:Backup:1:BackupAbility
SVar:BackupAbility:DB$ Pump | KW$ Double Strike
K:Double Strike
DeckHas:Ability$Counters
Oracle:Backup 1 (When this creature enters the battlefield, put a + 1/+ 1 counter on target creature. If that's another creature, it gains the following ability until end of turn.)\nDouble strike

View File

@@ -0,0 +1,9 @@
Name:Mirror-Shield Hoplite
ManaCost:R W
Types:Creature Human Soldier
PT:2/2
K:Vigilance
T:Mode$ BecomesTarget | ValidTarget$ Creature.inZoneBattlefield+YouCtrl | ValidSource$ Ability.Backup | TriggerZones$ Battlefield | Execute$ TrigCopy | ActivationLimit$ 1 | TriggerDescription$ Whenever a creature you control becomes the target of a backup ability, copy that ability. You may choose new targets for the copy. This ability triggers only once each turn.
SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredStackInstance | MayChooseTarget$ True
DeckNeeds:Keyword$Backup
Oracle:Vigilance\nWhenever a creature you control becomes the target of a backup ability, copy that ability. You may choose new targets for the copy. This ability triggers only once each turn.

View File

@@ -0,0 +1,10 @@
Name:Streetwise Negotiator
ManaCost:1 G
Types:Creature Cat Citizen
PT:0/2
K:Backup:1:BackupAbility
SVar:BackupAbility:DB$ Animate | staticAbilities$ CDT
SVar:CDT:Mode$ CombatDamageToughness | ValidCard$ Card.Self | Description$ This creature assigns combat damage equal to its toughness rather than its power.
S:Mode$ CombatDamageToughness | ValidCard$ Card.Self | Description$ This creature assigns combat damage equal to its toughness rather than its power.
DeckHas:Ability$Counters
Oracle:Backup 1 (When this creature enters the battlefield, put a +1/+1 counter on target creature. If that's another creature, it gains the following ability until end of turn.)\nThis creature assigns combat damage equal to its toughness rather than its power.

View File

@@ -0,0 +1,11 @@
Name:Voldaren Thrillseeker
ManaCost:2 R
Types:Creature Vampire Warrior
PT:1/1
K:Backup:2:BackupAbility
SVar:BackupAbility:DB$ Animate | Abilities$ ABDealDamage
SVar:ABDealDamage:AB$ DealDamage | Cost$ 1 Sac<1/CARDNAME/this creature> | ValidTgts$ Player,Creature,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SpellDescription$ It deals damage equal to its power to any target.
A:AB$ DealDamage | Cost$ 1 Sac<1/CARDNAME/this creature> | ValidTgts$ Player,Creature,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SpellDescription$ It deals damage equal to its power to any target.
SVar:X:Sacrificed$CardPower
DeckHas:Ability$Counters|Sacrifice
Oracle:Backup 2 (When this creature enters the battlefield, put two +1/+1 counters on target creature. If that's another creature, it gains the following ability until end of turn.)\n{1}, Sacrifice this creature: It deals damage equal to its power to any target.