-
Notifications
You must be signed in to change notification settings - Fork 152
2: The Fireplace DSL
Hearthstone defines sequences of actions that can happen at certain times. For example, a Battlecry will happen when a card was played from the hand. A Deathrattle will happen when a card with the DEATHRATTLE tag dies.
Those sequences can be defined in two ways. The recommended way is using the Fireplace Domain-Specific Language. Sequences which are too complex to be implemented using the DSL can be implemented as callables, which return an iterable of actions. An Action Sequence refers to an argument which can be an iterable of actions, or a callable that returns an iterable of actions.
The full Action reference is available here.
When generating a card dynamically is needed, such as summoning a random minion of some sort, card pickers can declaratively describe how the card should be picked.
There is a set of RandomPickers available, all building on top of fireplace.dsl.RandomCardPicker
:
RandomCard()
RandomCollectible()
RandomMinion()
RandomSpell()
RandomWeapon()
RandomSparePart()
Each of those pickers, as well as the base one, can take filters. For example, to give the controller a random Dream Card, you would do:
Give(CONTROLLER, RandomCard(card_class=CardClass.DREAM))
To summon a random legendary minion that costs 5 mana, you would do:
Summon(CONTROLLER, RandomMinion(cost=5, rarity=Rarity.LEGENDARY))
Copy()
is a picker that takes a selector as argument, and returns copies of the arguments it's given.
It can also be OR'd, in order to provide a fallback when the selector comes up empty:
Summon(CONTROLLER, Copy(RANDOM_ENEMY_MINION) | "CS2_231")
ExactCopy()
works like Copy()
, but will also copy buffs and various tags (damage, frozen, etc...).
When lazily evaluating a certain condition is required for a card's functionality, an Evaluator can be written. An Evaluator implements the evaluate(source, game)
method, which returns True
or False
, depending on its given conditions.
The following Evaluators are implemented:
-
Dead(selector)
: Returns True if every target matching the selector is dead. -
Find(selector, count)
: Returns True if the selector evaluates to at leastcount
entities.count
defaults to 1. -
Joust(selector1, selector2)
: Perform a Joust on the total cost betweenselector1
andselector2
. Returns True ifselector1
wins it.
An evaluator has an optional "then" clause, and an optional "else" clause. To describe those clauses, use the &
(binary and) and |
(binary or) operators, respectively. For example, to implement the Kill Command effect:
Find(FRIENDLY_MINIONS + BEAST) & Hit(TARGET, 5) | Hit(TARGET, 3)
The DSL supports some lazy numbers, which evaluate when the action runs. They can be used to multiply action or affect the amounts of other actions.
There are three LazyNum
implemented: Count()
, Attr()
and RandomNumber()
.
Count()
takes a selector
argument and will evaluate to how many matches there are. For example, to draw a card for every enemy minion:
Draw(CONTROLLER) * Count(ENEMY_MINIONS)
Attr()
takes a selector
and a tag
GameTag argument and will evaluate the sum of the tags matching the selector. For example, to hit a target with the total armor of your hero:
Hit(TARGET, Attr(FRIENDLY_HERO, GameTag.ARMOR))
RandomNumber()
takes two or more numbers and will randomly pick one of them at evaluation time. If it is evaluated multiple times within the same action, it is not guaranteed to be the same every time.
To hit enemy minions for 2-3 at random, you can do:
Hit(ENEMY_MINIONS, RandomNumber(2, 3))
Note that the parameters do not specifiy a range: RandomNumber(1, 3)
will explicitely roll either 1 or 3, and never 2.
When performing some operations on LazyNums
, they turn into a LazyNumEvaluator
, which lets them behave as an evaluator. For example, to draw if the friendly hero has at least 10 armor, you would do:
(Attr(FRIENDLY_HERO, GameTag.ARMOR) >= 10) & Draw(CONTROLLER)
Note that the parentheses are required due to Python's operator precedence.
Action callbacks to Events often reuse the arguments to the Event action in some way. In those cases, the named action parameters be used in place of what would be an entity or a number. For example, to give the controller a copy of a Minion that just died:
Death(MINION).on(Give(CONTROLLER, Copy(Death.ENTITY)))
Next article: Card introspection
- The Fireplace Card API
- The Fireplace DSL
- Card introspection
- Creating and playing a Game
- The CardDefs.xml file
- Developer Quick Start
- How to enable logging in Hearthstone
- Frequently Asked Questions
- Game State
- Game Actions
- Powers and Card Actions
- Target Selection
- Events
- Enchantments