Monday, May 9, 2011

Forge Scripting

The current developers have done a great job working on Forge.  (Which does not include me.)  The reason that Forge supports as many Magic cards as it does is through scripting or dsl (domain specific language).  When I stopped working on Forge only simple creature cards could be scripted.  Now instants, sorceries, and activated abilities can be scripted.

Before I jump in and show you some examples of scripting, let me briefly define it.  “Scripting is using a special, ‘small language’ inside of a bigger program.”  In the case of Forge, basically scripting is using a special syntax or language other than Java.  Scripting is shorthand.  Creating cards with Java is long and verbose.  Creating cards with scripting is short and sweet.

To see Forge’s scripting, look in your /forge/res/cardsfolder/ directory and to begin things Shock is always a good place to start.

Text:no text
A:SP$DealDamage | Cost$ R | Tgt$ TgtCP | NumDmg$ 2 | SpellDescription$ Shock deals 2 damage to target creature or player.

Above is most of the text in the file “shock.txt”.  And while I don’t understand all of the scripting language, you see it deals damage, has a mana cost, has some kind of target, and does 2 damage.

Name:Elvish Piper
ManaCost:3 G
Types:Creature Elf Shaman
Text:no text
A:AB$ChangeZone | Cost$ G T | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature | ChangeNum$ 1 | SpellDescription$ You may put a creature card from your hand onto the battlefield.

Elvish Piper is another well-known card.  This time the scripting adds an activated ability and a complicated one at that.  The scripting specifies that the ability will changes zones, has a tap cost, and the card that you choose in your hand has to be a creature and that it will be put onto the battlefield.  Since the ability is a complex one, I thought that this card may not be scripted but it is nice that the scripting language is powerful enough to not use Java. 

(Elvish Piper was one of the first cards that I ever coded for Forge.  I thought I understood the rules very clearly but I still made a small mistake.  I didn’t understand the difference between “target” and “choose” so I made the player choose a creature card in their hand BEFORE the ability when on the stack.  This is very incorrect as the choice should be made AFTER the ability resolves.)

Name:Eon Hub
Text:Players skip their upkeep steps.

The text above is for Eon Hub but it does not have any scripting because it is specified in the Java code.  Scripting tries to deal with common actions not unique effects.

Text:no text
A:SP$ChangeZone | Cost$ B | Origin$ Library | Destination$ Graveyard | ChangeType$ Card | ChangeNum$ 1 | SpellDescription$ Search your library for a card and put that card into your graveyard. Then shuffle your library.

Entomb is another good example.

ManaCost:3 B
Text:no text
A:SP$ChangeZone | Cost$ 3 B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | SpellDescription$ Return target creature card from your graveyard to the battlefield.

Zombify is similar to Entomb but the syntax is a little different because Zombify restricts you to only choosing creature cards.

Name:Wrath of God
ManaCost:2 W W
Text:no text
A:SP$DestroyAll | Cost$ 2 W W | ValidCards$ Creature | NoRegen$ True | SpellDescription$ Destroy all creatures. They can't be regenerated.

Wrath of God is a personal favorite of mine.

Name:Glorious Anthem
ManaCost:1 W W
Text:no text
K:stPumpAll:Creature.YouCtrl:1/1:No Condition:Creatures you control get +1/+1.

Pumping up your creatures is another common effect.  This time the scripting creates a continuous static effect versus just a spell or ability.

Name:Wrath of Marit Lage
ManaCost:3 U U
Text:Red creatures don't untap during their controllers' untap steps.
K:Permanents don't untap during their controllers' untap steps:Creature.Red
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTapAll | TriggerDescription$ When CARDNAME enters the battlefield, tap all red creatures.
SVar:TrigTapAll:AB$TapAll | Cost$ 0 | ValidCards$ Creature.Red

And to wrap things up with, Wrath of Marit Lage.  This effect is very complicated and I’m surprised to see it scripted.

Forge’s scripting is the reason that last month 1,000 new cards were added.  Creating a scripting language is hard because you want it to be flexible but also easy to read.  “Easy to read” both as a programmer and “easy to read” as in “easy for the code to parsed”. 

(Parsing is transforming the shorthand into longhand and parsing can be very, very complicated and thorny.  There is no end to the parsing rabbit hole, which is why you try to make your scripting language easy to parse.)

I don't know if it is really scripting or not but Forge also puts the card rarity with the other card information.

Thanks for reading,


tehdiplomat said...

One other key advantage of Scripting that you didn't mention is consistent AI play. When we update the DealDamage portion of the code to be more picky about what it targets, every single card that uses DealDamage will now be improved across the board. If a card is hardcoded each card that is hardcoded would need to be updated separately.

I would consider the Rarity (and other similar non-scriting information) to be metadata. Information about the card that makes sense to be attached to the card, but doesn't necessarily DO anything.

--friar sol

Forge said...

The AI is very important and the Forge programmers use scripting for some AI stuff. (I don't know exactly.)

"I would consider the Rarity (and other similar non-scripting information) to be metadata."

It probably is metadata but it has to be put somewhere. Initially I ignored rarity all together because different sets may have the same card at different rarities. In reality it doesn't matter and people want to play Standard.