RIP Grim Patron

After several months of high activity on Fireplace, things have slowed down a little.

I have been quite busy preparing a move to Greece, which left me little time to update the blog or do further work on Stove. However, there has still been a lot of activity throughout HearthSim.

HearthSim projects

Most recently, both HearthstoneJSON and Hearthstone Deck Tracker have joined our ranks. In addition, I have also moved the python-bna project, a Python implementation of the Battle.net Authenticator, to HearthSim ownership.

We have also opened an “official unofficial” issue tracker for Hearthstone: HearthSim/hs-bugs. It lets us keep track of bugs using a much saner interface than a… wiki. Incidentally, it has proven itself quite popular since it allows for easy references to known issues and lets us track whether bugs have been fixed and if so, when.

@Xinhuan has created a CardBot for the IRC channel. It allows quickly finding details on cards with !card <id>, !card <name> or !card <partial-match>. Join our channel to try it out! :)

With the help of @Epix37, we have done a lot of work on the HSReplay spec which I have just finished licensing as CC0 (implementations MIT). At this point, they are essentially complete and heavily battle-tested. This means they will be bumped to 1.0 very soon, which will feature an official announcement on this blog.

Due to needs in other projects (HSReplay specifically), the Hearthstone enums have been separated out of Fireplace and into a python library of its own: python-hearthstone. The package is semantically versioned with the Hearthstone build as PATCH number. This allows for both releasing changes in the package on multiple HS builds as well as supporting new builds without any other change in the code.

Fireplace work

Activity on Fireplace died down mid-september, but I have been trying to squeeze in some minor work as regularly as possible.

The Kettle test games have highlighted several issues in the game logic which could not be caught with everyday usage or testing. For example, weapon.exhausted would always be True on the opponent’s turn regardless of their Zone, We caught this because Kettle has to update tags on all entities every game tick, so it would send loads of unnecessary TAG_CHANGE packets for EXHAUSTED every turn.

Another fun bug was Give() ignoring the hand limit. There’s a screenshot of this somewhere…

We also resolved issues with targeting checks. More specifically, cards that didn’t have a target would accept any target and subsequently ignore it, which meant that Hearthstone would show a targeting arrow for eg. Life Tap. This also occured on Battle Rage and Tinker’s Sharpsword Oil which both include REQ_MINION_TARGET but no actual targeting requirement, probably as an artifact of previous versions of the spells.

In the quality of life category, the logging system has been improved and made much more readable.

The implemented.py script hasi also gained a lot of functionality which makes it a lot more accurate. It will now output the % of implemented cards. Are you curious? It’s 92%! Taking into account that most of the missing ones are adventure or unused cards, I’m very proud of that number. With The Grand Tournament’s release, I ended up pushing to implement all the cards that could be “dry-coded”. This means we are missing a lot of tests, but at least they are usable and can catch issues.

Some slots reworking has been leading towards a rework of buffing. This is in fact part of a push to get rid of the CardXML pre-processing, which has been detrimental to the out-of-the-box experience, especially on Windows. The latter was helped a lot when @liujimj contributed a Powershell bootstrap script.

In another “Really? It took that long?” twist, playing minions at an index has now been implemented. It is a product of continuous design: Work long enough on a project with a hard feature in mind, and the feature will eventually materialize itself.

Finally, delayed event triggering is a new model @Patashu and I have been testing. I am very happy with it and have implemented it in Fireplace for Heal(). Damage still has to be implemented but there is some design work to finish on Predamage first (for Ice Block and Bolf, and I suspect Divine Shield too). Deaths will also move to that system once I’m happy with how it works.

DSL changes

The biggest change by far has been the fifth Aura API. It was motivated by both Kettle and the bootstrap pains. Having “real” cards use custom cards as their buffs meant crashes in the Hearthstone client. It could be filtered out in Kettle but it’s much easier to get rid of most of them; this move took a lot of complexity out of creating cards that have their own auras. It also highlighted how easy it is to model certain more complex cards under the aura model. For example:

Retarget() has been implemented. It it used for both Noble Sacrifice and Spellbender.

Buff() now accepts **kwargs.

@beheh reworked mana gains and implemented the GainEmptyMana() action. This removes the need to call FillMana() after GainMana(). Instead, GainMana() stands for “Gain a Mana Crystal” and GainEmptyMana() stands for “Gain an empty Mana Crystal”.

The new CurrentPlayer() evaluator will return True iff the controller is the current player, which finally allows for Worshipper’s implementation with the new aura API:

class NAX2_05:
	update = CurrentPlayer(CONTROLLER) & Refresh(FRIENDLY_HERO, {GameTag.ATK: +1})

Two new card scripts:

  • draw, for actions triggered when the card is drawn (Flame Leviathan etc).
  • powered_up, an evaluator which checks whether the card is POWERED_UP. This replaces the nasty old PowerUpRequirements mechanism.

The cost script has also been replaced by cost_mod which understands LazyNums.

Three new helpers:

  • COINFLIP (“50% chance to …”). Example: COINFLIP & Draw(CONTROLLER)
  • CLEAVE (“Also damages the minions next to whomever he attacks”). Usage: Attack(SELF).on(CLEAVE)
  • EMPTY_HAND (“If your hand is empty, …”). Example: EMPTY_HAND & Draw(CONTROLLER)

R.I.P. Grim Patron

99 files changed, 3601 insertions(+), 1976 deletions(-)

Jerome