Always wins Brawls.
This past week has been one of the most active in Fireplace. Loads of new cards, improvements to the DSL, massive cleanups and more.
Part of the focus, with the growing attention on the simulator, was to improve the new contributor experience. So I wrote a lot of new documentation, as that was a major pain point. Fireplace’s documentation was mostly outdated since the last event rewrite. I’m not done with it; I would like to have the DSL fully documented before doing an official public release.
The other main focus was on the Fireplace DSL… which now looks a lot more like a real DSL. It is officially on version 3 now.
The major changes are:
actionhas been renamed to
- Sequences now support single actions, as well as falsy values in the case of callables
For example, this:
action = [Destroy(TARGET)]
Is now this:
play = Destroy(TARGET)
Or in the more complex case of Trade Prince Gallywix, this:
events = [ Play(OPPONENT, SPELL).on( lambda self, player, card, *args: card.id != "GVG_028t" and [ Give(player.opponent, card.id), Give(player, "GVG_028t") ] or  ) ]
Is now this:
events = Play(OPPONENT, SPELL).on( lambda self, player, card, *args: card.id != "GVG_028t" and ( Give(player.opponent, card.id), Give(player, "GVG_028t") ) )
Together, these changes affect almost every single card. Big diffs!
New functions are also available in the DSL.
The first one is
# Mind Control Tech class EX1_085: play = Find(ENEMY_MINIONS, 4) & Steal(RANDOM_ENEMY_MINION) # Kill Command class EX1_539: play = Find(FRIENDLY_MINIONS + BEAST) & Hit(TARGET, 5) | Hit(TARGET, 3) # Upgrade! class EX1_409: play = ( Find(FRIENDLY_WEAPON) & Buff(FRIENDLY_WEAPON, "EX1_409e") | Summon(CONTROLLER, "EX1_409t") ) # Tinkertown Technician class GVG_102: play = (Find(FRIENDLY_MINIONS + MECH) & ( Buff(SELF, "GVG_102e"), Give(CONTROLLER, RandomSparePart()) )) # Ethereal Arcanist class EX1_274: events = OWN_TURN_END.on(Find(FRIENDLY_SECRETS) & Buff(SELF, "EX1_274e"))
It is an extremely powerful evaluator that allows an if/else condition through the use of
shortly after, using similar logic:
# Bane of Doom class EX1_320: play = Hit(TARGET, 2), Dead(TARGET) & Summon(CONTROLLER, RandomMinion(race=Race.DEMON))
# Harrison Jones class EX1_558: play = ( Draw(CONTROLLER) * Attr(ENEMY_WEAPON, GameTag.DURABILITY), Destroy(ENEMY_WEAPON) )
Attr uses similar logic as
Count, and has been implemented as a
LazyNum, which means it
can be used for both action multipliers and action arguments.
The last addition is
events = Play(OPPONENT, SPELL - ID("GVG_028t")).on( lambda self, player, card, *args: ( Give(player.opponent, card.id), Give(player, "GVG_028t") ) )
Almost fully declarative. There is no way to get the
card argument dynamically, unfortunately. Yet. :)
Deathrattle() action has also been added. It it used internally in place of the old
It forced some cleanups related to the
EXTRA_DEATHRATTLES tag, and allows us to implement
Feign Death very elegantly:
TakeControl has been renamed to
# Resurrect class BRM_017: play = Summon(CONTROLLER, Copy(RANDOM(FRIENDLY + KILLED + MINION)))
Phew. That was a lot of changes.
This week’s Tavern Brawl was very easy to implement. I thought it was a bit boring as a Tavern Brawl, but an excellent idea for a more “permanent” game mode.
But really, I am writing this to talk about the regular kind of Brawls.
Dark Iron Bouncer has been implemented, using a custom
ALWAYS_WINS_BRAWLS tag, which got its own selector. And now, the Brawl spell looks like this:
# Brawl class EX1_407: play = ( Find(ALL_MINIONS + ALWAYS_WINS_BRAWLS) & Destroy(ALL_MINIONS - RANDOM(ALL_MINIONS + ALWAYS_WINS_BRAWLS)) | Destroy(ALL_MINIONS - RANDOM_MINION) )