DSLs, Stove and The Grand Tournament

Another very busy week in Fireplace! New contributor @beheh is joining us and already contributed dozens of new tests.

Part of that work involved implementing an easy way to create an “empty” game which would start with empty decks and prevent fatigue. This allows for easily testing draw-based events such as Malorne.

The Grand Tournament

A new expansion has been announced! With a new keyword: Inspire!

Well, turns out the INSPIRE GameTag was already in the game and all I had to do was implement it.

So that’s done. Most of the announced cards are done, as far as non-static data goes anyway.

DSL changes

Contributed by @zakum42, the RandomNumber LazyNum which allows for elegantly implementing cards such as Lightning Storm:

play = Hit(ENEMY_MINIONS, RandomNumber(2, 3))

The Attr() LazyNum has gained comparison capabilities, which turn it into an Evaluator. This can be used to implement cards which behave differently based on the total of an attribute, such as Mortal Strike:

play = (Attr(FRIENDLY_HERO, "health") <= 12) & Hit(TARGET, 6) | Hit(TARGET, 4)

The ExactCopy() picker has been implemented. It functionally works like Copy(), but recreates buffs, tags etc. Faceless Manipulator is very elegant:

play = Morph(SELF, ExactCopy(TARGET))

The Counter() action, used by Counterspell, has been implemented as well. As it turns out, all it does is set a CANT_PLAY tag on the card, which prevents its play action from triggering. The Wild Pyromancer interaction does not happen like in Hearthstone, however, so that will have to be fixed.

events = Play(OPPONENT, SPELL).on(Counter(Play.Args.CARD), Reveal(SELF))

Another new action: UnlockOverload(), for Lava Shock. Not much to say about that one.

There were two important organizational cleanups regarding the DSL. The first one that hit was moving it to fireplace.dsl. This forced clearing up some vocabulary on what is an Evaluator, a Picker etc. Everything is available from the root of the module, but it’s now subdivided like this:

  • fireplace.dsl.evaluator: Evaluator, Dead(), Find()
  • fireplace.dsl.lazynum: LazyNum and LazyNumEvaluator, Attr(), Count(), RandomNumber()
  • fireplace.dsl.picker: Picker, Copy(), ExactCopy(), RandomCardPicker()
  • fireplace.dsl.selector: All the selectors and their logic

The other significant cleanup was getting rid of source, game pairs in favour of just source and reusing source.game.

Engine and simulation

On the game simulation side, Play processing has been redone. This is possibly its fourth major rewrite. I am happy with the results, although it looks like to comply with Hearthstone, I have to pause aura updates. I don’t think this makes any sense but it does look like Patashu is correct and there is no way around it: Auras don’t update during Play.

Finally, Attack/Health swaps are now handled in-engine. I’m not actually a fan of this, but it seems like Hearthstone loves that mechanic, so it’s simpler that way.

The two new brawls, Heart of the Sunwell and Too Many Portals! are also done. They were not particularly hard. The former looks a lot like my BaseTestGame class. :)

Other repositories

The biggest cleanup this week by far has been removing the hs-cards submodule. Submodules in git kind of suck (a lot). Chugging it around created its fair share of problems, especially since all the static data changes were going in that repository. That meant that when you wanted to implement a new card, you had to send a pull request to two separate repositories. Urgh.

Well, that’s gone. Static data is now in fireplace/cards/data/ and all the post-processing that goes with it. Even Hero Power associations are now automatically detected from the static files, where they previously had to be manually assigned.

We now use a different repository, hs-data. This one contains normalized, cleaned up files from the game, both DBF and CardDefs. Each patch is a commit, and each commit is tagged by its build number. So if you wanted to see the diffs from build 9554, you could look at the tag directly.

The extraction scripts used to generate that repository are available in the HearthSim/extract-scripts repository. I even had to update my StormLib Python bindings for the occasion, so I moved them to the HearthSim organization as well.

Most of all this work comes from the needs of Stove, the Hearthstone server @mischanix and I are currently working on. I am really happy with the progress on it. We have a lot of the main menu implemented, including collection management and booster packs. Everything is running on top of a SQLite database which automatically imports most of its contents from the aforementioned hs-data DBFs.

What a month July was.

71 files changed, 4282 insertions(+), 1118 deletions(-)