-
Notifications
You must be signed in to change notification settings - Fork 122
Serialization Format
The following is proposal for a serialization format of the current board state of a game in Heartstone. It makes no assumptions about the state of the game, (i.e. the game can be serialized or deserialized part of the way through a turn, or at the end or beginning of a turn).
The proposed format is a JSON based format. In most cases it relies on standard Hearthstone Terminology, but a standard for effect names and descriptions will have to be agreed upon.
Some of the objects could possibly be merged into other objects, but are kept separate for discussion's sake. Later on, they can be combined if necessary (i.e. Deck into an array instead of an array inside an object)
At the root is the game object, which consists of an array of two Player objects and an integer which gives the index of the player whose turn it is now:
{
"players": [Player, Player]
"active_player": Integer
"current_sequence_id": Integer
}
current_sequence_id
is an integer to keep track of when minions are created. Each time a new minion is placed, current_sequence_id
is incremented, and the minion's sequence_id
is set to the new value. This allows for proper execution of deathrattles.
The Player object houses all information about a Player:
{
"hero": Hero,
"deck": Deck,
"hand": [Card, Card, ...],
"secrets": [Secret, Secret, ...]
"card_filters": [CardFilter, CardFilter, ...],
"minions": [Minion, Minion, ...]
"mana": Integer,
"max_mana": Integer
}
The Hero object refers to the avatar of the player in game:
{
"character": "Mage" | "Paladin" | "Druid" | ... ,
"weapon": Weapon,
"health": Integer,
"attack": Integer,
"immune": Boolean,
"frozen_for": Integer,
"windfury": Boolean,
"used_windfury": Boolean,
"already_attacked": Boolean
}
Most properties are fairly self explanatory. frozen_for
is an integer that specifies how many of the player's turns this hero will be frozen for. If the hero is frozen on the owning player's turn, then it will be frozen for the remainder of that turn, and the next one, so frozen_for
will be 2
. If it is frozen on the opposing player's turn, then frozen_for
will be 1
. At the end of the owning player's turn, frozen_for
will be decremented, until it is 0, and the hero is no longer frozen.
If a hero's windfury
is set to True
, then the first time the hero attacks in a turn, used_windfury
will be set to True
. The second time, already_attacked
will be set to True
. This gives a way to know if a can still attack.
The Weapon object simply gives the name of the weapon and its stats. Any effects (such as from Gladiator's Longbow) can be inferred from the weapon's name
{
"name": String,
"attack": Integer,
"durability": Integer
}
The Deck object gives information about what cards are left in the deck. It is an array of DeckCard objects:
{
"cards": [DeckCard, DeckCard,...]
}
A DeckCard object is simply the name of the card. Currently, this does not record which cards have already gone by
{
"id": Integer
"name": String
}
The id
property must be unique across DeckCards and Cards.
The Card object gives information about a card currently in the Player's hand.
{
"name": String
"id": Integer
}
The id
property should be the same as when the card was still in the deck
The Secret object states information about secrets that a player might have
{
"name": String
}
The CardFilter object is designed to change the mana cost of one or more of the player's cards. These filters are not generated as an aura effect by a minion on the board, but are generated as an effect (such as battlecry, deathrattle or a spell) Examples include Frozen Trap, or Loatheb's Battlecry.
{
"amount": Integer,
"filter": "minion" | "spell" | "secret" | "card",
"until": "turn_ended" | "turn_started",
"player": 1 | 2
}
amount
is the amount to decrease the mana cost of affected cards
card_filter
is the type of cards to affect. Possible values are "minion", "spell", "secret" and "card"
until
: When to remove this effect. Possible values are are "turn_started" for the start of the next turn and "turn_ended" for the end of the current turn. The filter will be removed regardless of the only_first
property.
only_first
True if this card filter should be removed the first time a player plays a card which matches the filter
The Minion object represents a minion as placed on the board.
{
"name": String,
"sequence_id": Integer,
"position": Integer,
"health": Integer,
"max_health": Integer,
"attack": Integer,
"stealth": Boolean,
"taunt": Boolean,
"divine_shield": Boolean,
"spell_targetable": Boolean,
"windfury": Boolean,
"charge": Boolean,
"exhausted": Boolean,
"already_attacked": Boolean,
"windfury_used": Boolean,
"frozen_for": Integer,
"immune": Boolean,
"silenced": Boolean,
"effects": [Effect, Effect, ...]
}
Most properties are fairly self explanatory, and frozen_for
, windfury_used
and already_attacked
work similarly to how they work for Hero
.
sequence_id
is an integer that indicates when this minion was created, for the purpose of executing deathrattles. See the discussion in the Game
object.
position
is a zero based index of where the minion is on the board.
An effect describes something that has happened to a minion, either from its own text, or as a result of a spell or battlecry. Aura effects from other minions are not counted, except on the minion that is the source. For example, if a Stormwind Champion is down, each other minion on the board won't have a "PlusOnePlusOne" effect applied, but StormwindChampion will have a "GiveFriendlyMinionsPlusOnePlusOne" effect on it.
{
"name": String
}
The name of the effect is used as a dictionary key to look up the effect and apply it. So, it is necessary to have a standardized list of possible effects. The names would be a description of what happens, like "DestroyRandomMinion" or "GiveAdjancentMinionsPlusTwo".
Another possibility is to have a more complex effect object (similar to the effect system in Focus) such as
{
"type": "stealth",
"until": "end_turn"
}
or
{
"type": "increase_attack",
"filter": "adjacent_minions",
"amount": 2
}
This gives much more flexibility, and will allow for new effects without needing to add to the list of effects in the dictionary. It has the downside of being more complex.