Skip to the content.

Heroes of the Storm Replay Data Reference

This document details the location and type of every useful piece of data in the Hereos of the Storm replay files to the best of the my knowledge. Let me know if you find any errors. - Falindrith

Common Elements

In this document, fields with [] at the end indicate that the element in question is an array, and that any field named after the [] (m_playerList[].m_toon for instance) is assumed to exist for all elements in the array.

Containers

Most of the locations reference a container object first (header for instance). These are the objects that are available from the parser. In whatever parser you choose to use, you should be able to specify the extraction of these containers. This document assumes that you are using the heroprotocol.js parser.

Game Loops to Game Time

Almost every event time in the game is stored in terms of game loops. Game loops run 16 times per second and can be converted to seconds with loops / 16. Note that this won’t actually match the displayed elapsed game time due to the in-game clock having a negative time offset of exactly 610 frames. So to get the actual in-game time of any event, use a function like:

function loopsToGameSeconds(loops) {
    return (loops - 610) / 16;
}

fixedData

Data stored in m_fixedData fields are fixed precision integers. Divide by 4096 to get the actual value.

Reserved Tracker PlayerIDs

The events in trackerevents are usually associated with a PlayerID. These are typically the human controlled players in the game, however the system reserves PlayerIDs 11 and 12 for internal use. PlayerID 11 is the Blue Team’s AI player, which manages minions, buildings, and any non-player controlled units associated with the blue team. Likewise, PlayerID 12 is the Red Team’s AI player.

Human players should never get assigned to these IDs. If your match has observers in them, they could have IDs 13+, but will not conflict with the reserved IDs.

Hey where are the Quest Stacks?

Not in the replay that’s for sure. For whatever reason, the state of quests and quest completions are not stored in the replay. Some units may have Upgrade Tracker events associated with them, but they are very inconsistently used. So until Blizzard adds these events into the replay, the only way to reconstruct quest stacks is to simluate the game based on the input events in GameEvents and track the results.

Tracking Unit Positions

Some events have positional data associated with them. You can use this to track where a unit is when the event happens. For more details, you should look at barrett777’s parser.

Match Basics

Version

Location: header.m_version

Value: Object

{
    "m_baseBuild": [int],
    "m_minor": [int],
    "m_revision": [int],
    "m_flags": [int],
    "m_major": [int],
    "m_build": [int]
}

Reconstruct game version: m_major.m_minor.m_revision

Elapsed Time

Location: header.m_elapsedGameLoops

Value: The total time of in elapsed game loops. THIS IS NOT THE ACTUAL MATCH LENGTH. To properly determine match length, you will need to track when one of the team’s cores dies.

Map

Location: details.m_title

Value: Localized map name.

Date

Location: details.m_timeUTC

Value: Date in Windows File Time Format. This is incredible for a number of reasons but the tl;dr version is to convert this with the following javascript function:

function winFileTimeToDate(filetime) {
  return new Date(filetime / 10000 - 11644473600000);
}

Mode

Location: initdata.m_syncLobbyState.m_gameDescription.m_gameOptions.m_ammId

Value: An integer.

{
  "50021": "Versus AI",
  "50041": "Practice",
  "50001": "Quick Match",
  "50031": "Brawl",
  "50051": "Unranked Draft",
  "50061": "Hero League",
  "50071": "Team League",
  "-1": "Custom"
}

Players

Player data is scattered all over the replay file and there are a variety of different IDs used to reference who did what when.

Player IDs

There are multiple player IDs used by the replay file, and the often don’t end up matching each other.

Toon

Location: details.m_playerList[].m_toon

Value: Object

{
    "m_id": [int],
    "m_realm": [typically the number 1],
    "m_region": [one of: 1 (NA), 2 (EU), 3 (Asia?), 98 (PTR or Tournament Realm)],
    "m_programId": "Hero"
}

To create a ToonHandle: m_region + '-' + m_programId + '-' + m_realm + '-' + m_id

The Toon object can be used to create the ToonHandle for a player. ToonHandles are unique identifiers assigned per-player per-region. This means that the same player will have a different toon ID on a different realm. ToonHandles should be your internal way to track players, as it is immutable for each player. ToonHandles cannot be used to identify players in the trackerevents or basically anywhere else in the replay file. You will need to use the Tracker ID or other ID as detailed below.

Tracker PlayerID

Location: An event in trackerevents with event ID 10 (stat) and event name PlayerInit

When parsing the trackerevents, any event that references a PlayerID will be using the values contained in the PlayerInit events. Relevant data for these events:

Working Set Slot ID

Location: details.m_playerList[].m_workingSetSlotId

Value: Int

This is, as far as I can tell, only relevant for determining pick order in trackerevents. Using this ID anywhere else results in errors where players can’t be mapped back to a proper ToonHandle. See the Draft section for more details.

Lobby ID

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_userId

Value: int

Used by gameevents and messageevents to determine actions and messages sent by players. ToonHandle is accessible in this array with initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_toonHandle.

Name

Location: details.m_playerList[].m_name

Value: String

Hero

Location: details.m_playerList[].m_hero

Value: Localized hero name.

Note: For Lucio, it actually includes the accent over the u, so make sure your hero keys are set properly, otherwise you’ll have issues identifying that one.

Silence Penalty

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_hasSilencePenalty

Value: bool

Voice Silence Penalty

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_hasVoiceSilencePenalty

Value: bool.

May not exist pre-build 62424.

Cosmetics

Contained in the lobby state slots.

Skin

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_skin

Value: String. Internal Skin ID.

Announcer

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_announcerPack

Value: String. Internal Announcer ID.

Mount

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_mount

Value: String. Internal Mount ID.

Spray

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_spray

Value: bool

Is Blizzard Staff

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_isBlizzardStaff

Value: bool

Has Active Boost

Location: initdata.m_syncLobbyState.m_lobbyState.m_slots[].m_hasActiveBoost

Value: bool As of 2.40

Talents

See EndOfGameTalentChoices Tracker Event.

Draft

Draft data is consistent from HotS 2.0 on, but somewhat unreliable pre-2.0

Bans

Location: attributeevents.scopes["16"]

Value: Object containing a number of cryptic keys.

Relevant data:

Each of these objects returns a hero Attribute value (typically the first 4 letters of the hero’s internal name, which doesn’t usually match up with the display name). Recommended to use heroes-talents to resolve these names. Note that in attribute events, scopes is an object, but the numeric key is an array of size 1 with an object in it.

Alternate Location: Events in trackerevents with _event value NNet.Replay.Tracker.SHeroBannedEvent. This event contains the hero banned in event._m_hero and the team that banned it in event.m_controllingTeam. Blue is 1, Red is 2. m_hero contains the internal name of the hero (e.g. FaerieDragon for Brightwing).

Picks

Location: Events in trackerevents with _event value NNet.Replay.Tracker.SHeroPickedEvent

Value: Object.

Relevant data:

Atrribute Hero Name

Localized hero names and internal hero IDs are used in many places of the replay. To resolve issues with this, use the attribute ID located in attributeevents.

Location: attributeevents.scopes[{Player Tracker ID}]["4002"][0].value

Use the heroes-talents repository data to resolve attribute id to hero name.

Hero Level

Location: attributeevents.scopes[]["4008"][0].value

Blizzard never updated this after HotS 2.0, so we’re currently stuck at hero level 20.

Other Attributes

See barrett777/Heroes.ReplayParser for a list of more attribute values.

Tracker Events

Tracker events are stored in the trackerevents array. This is a long array of individual event objects. Each tracker object must have the following fields:

{
    "_event": [string],
    "_eventid": [int],
    "_gameloop": [int]
}

Events will have additional fields depending on the value of the _eventid field. The _event string is a textual version of _eventid so it’s probably faster to just use _eventid to case on events. In this section, the event object will be referred to as event.

May of the maps have tracker events that detail who won the objective, however there are some notable exceptions. Maps which either lack or have complex ways to detect objectives:

Tracker Event List

Events of interest are linked to the corresponding section

_eventID Name Notes
1 Unit Born  
2 Unit Died  
3 Unit Owner Change  
4 Unit Type Change  
5 Upgrade I think certain heroes get this event when a quest is complete? In my opinion this should be emitted every time part of a quest or the entire quest is completed but it is not. Blizz pls.
6 Unit Init  
7 Unit Done  
8 Unit Positions The heroprotocol repository has additional info about this event.
9 Player Setup This is different than the Stat PlayerInit event and is not used by my parser
10 Stat The majority of interesting data is stored in these events
11 Score  
12 Unit Revived  
13 Hero Banned  
14 Hero Picked  
15 Hero Swapped Indicates a swap. Not really necessary since HeroPicked has the right associated hero regardless of swaps

Unit Born (Nnet.Replay.Tracker.SUnitBornEvent), _eventid = 1

Shows up when a unit spawns. These events all have basically the same info.

Contents:

Useful Unit Type Names

The following list is non-exhasutive, but it should contain most of the units of interest.

General Units

Unit Type ID Notes
KingsCore The Core
VanndarStormpike Blue Core on Alterac Pass
DrekThar Red Core on Alterac Pass
FootmanMinion Melee Minion
RangedMinion  
WizardMinion The one that drops a regen globe
RegenGlobe  
RegenGlobeNeutral These are the purple ones.
UnderworldSummonedBoss Haunted Mines golems. This is not the single boss in the shrines, it is the golems that spawn for each team.
RavenLordTribute Cursed Hollow Tribute
DragonShireShrineSun Sun Shrine. Can track which team controls. See Beacon Control.
DragonShireShrineMoon Moon Shrine. Can track which team controls. See Beacon Control
VehiclePlantHorror Garden Terror
VehicleDragon Dragon Knight
SoulEater Webweaver
VolskayaVehicle Triglav Protector
NukeTargetMinimapIconUnit This appears to be the targeting circle that appears when a nuke is channeled. See Warhead Junction for details
ZergHiveControlBeacon One of the Braxis control points. Can track which team controls. See Beacon Control.
ZergPathDummy When the zerg wave spawns, this unit shows up in Unit Born events
BossDuelLanerHeaven Heaven Immortal
BossDuelLanerHell Hell Immortal
WarheadSingle A standard warhead spawn
WarheadDropped A warhead dropped by a player after they die.
HealingPulsePickup The healing pulse item on some of the newer maps
TurretPickup The turret item on some of the newer maps
Payload_Neutral Neutral Payload unit (Hanamura Temple). Can track unit ownership to figure out who’s on the payload.

Mercenaries

Units listed here are the ones that spawn in lanes. There are different units for the neutral unit types that spawn at camps. Save the tag IDs to match up with death events to find out how long they were alive.

Unit Type ID Notes
TerranHellbat Hellbat Merc Camp (also the Alterac Pass Siege Camp)
TerranArchangelLaner Braxis boss
TerranGoliath Goliath Merc Camp
SlimeBossLaner Warhead boss
JungleGraveGolemLaner Cursed/Sky/Tomb/Alterac boss
MercLanerSiegeGiant Siege Camp
MercGoblicSapperLaner Goblin Sapper
MercSiegeTrooperLaner Bruiser Camp, on some of the maps
MercSummonerLaner Summoning Camp
MercSummonerLanerMinionDummy Summoning Camp summon, there are a lot of these
MercLanerRangedOgre Bruiser Camp unit (i think?)
MercLanerMeleeOgre Bruiser Camp unit
MercLanerMeleeKnight Knight Camp Unit
MercLanerRangedMage Knight Camp Mage Unit
MercLanerSentinel Sentinel (Hanamura Temple Siege camp)

Structures

Unit Type ID Notes
TownTownHallL2 Fort (L2)
TownMoonwellL2 Fort Well (L2)
TownCannonTowerL2 Fort Tower (L2)
TownTownHallL3 Keep (L3)
TownMoonwellL3 Keep Well (L3)
TownCannonTowerL3 Keep Tower (L3)

Braxis Units

Unit Type ID Notes
ZergZergling Zergling
ZergBaneling Baneling
ZergHydralisk Hydralisk
ZergGuardian Guardian
ZergUltralisk Ultralisk

Minion XP Tables

Just going to copy the minion XP arrays from the code. Tomb of the Spider Queen apparently has a different XP table for everything except catapults. Maxes out at 30 minutes. Minions are assigned XP values at Unit Born time (not when they die, so you can’t like stack up minions and get bonus XP).

const MinionXP = {
  FootmanMinion:  [70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 106],
  WizardMinion:   [62, 64, 66, 67, 69, 71, 73, 75, 76, 78, 80, 82, 84, 85, 87, 89, 91, 93, 94, 96, 98, 100, 102, 103, 105, 107, 109, 111, 112, 114, 116],
  RangedMinion:   [60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120],
  CatapultMinion: [1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37]
};

const TombMinionXP = {
  FootmanMinion: [55, 56, 57, 58, 59, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77 ,79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 90, 92],
  WizardMinion:  [51, 53, 55, 56, 58, 60, 62, 64, 65, 67, 69, 71, 73, 74, 76, 78, 80, 82, 83, 85, 87, 89, 91, 92, 94, 96, 98, 100, 101, 103, 105],
  RangedMinion:  [51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111],
  CatapultMinion: [1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37]
};

Unit Died (NNet.Replay.Tracker.SUnitDiedEvent), _eventid = 2

Fired when a unit dies. This includes structures, which are just units that don’t move.

Contents:

Unit Owner Change (NNet.Replay.Tracker.SUnitOwnerChangeEvent), _eventid = 3

Fired when a unit gets controlled by a different player. Typically, this is used with vehicles (dragon knight, garden terror, protector, etc.).

Contents:

This event can be used to determine who controls a vehicle (garden terror, dragon knight), or a beacon/shrine on Braxis/Dragon Shire. See the Beacon Control for additional details.

Stat Events (NNet.Replay.Tracker.SStatGameEvent), _eventid 10

Stat events all have an additional m_eventName parameter which determines what data they contain. A list of relevant game events follows. For all of these objects, _eventid = 10.

Player Init

Event Name: PlayerInit

Every player that controls a Hero in the game is initialized here. Observers do not show up. The ID value referenced here is used in future Stat Events and can be assodicated with a ToonHandle to uniquely identify players.

Contents:

Gates Open

Event Name: GatesOpen

This event marks match time 0:00. So far, every single match has started 610 loops after loading, but if that value changes, use this event to determine which loop is at 0:00.

No relevant contents. Use _gameloop to access the match start loop.

End of Game Talent Choices

Event Name: EndOfGameTalentChoices

Contents:

Periodic XP Breakdown

Event Name: PeriodicXPBreakdown

Contents (XP values are in fixed data, divide by 4096 to get correct values):

These events are found every minute in the replay file.

End of Game XP Breakdown

Event Name: EndOfGameXPBreakdown

Contents:

For some reason this isn’t attached to teams and instead is written per-player. As far as I can tell, this is the same for every player.

Player Death

Event Name: PlayerDeath

Contents:

Regen Globe Picked Up

Event Name: RegenGlobePickedUp

Contents:

Level Up

Event Name: LevelUp

This is fired once per player. So every time a team levels, up, you’ll get like 5 of these events.

Contents:

Mercenary Camp Captured

Event Name: JungleCampCapture

Fired when a team captures any mercenary camp.

Contents:

Special note for Towers of Doom: If the Camp Type is Boss Camp on that map, that’s the boss that fires 4 shots at the core.

End of Game Upvote

Event Name: EndOfGameUpVotesCollected

This keeps a running tally of the number of upvotes each player got. You can use it to identify both who got the vote and who gave the vote.

Contents:

Spray Used

Event Name: LootSprayUsed

Contents:

Voice Line Used

Event Name: LootVoiceLineUsed

Contents:

Sky Temple: Shot Fired

Event Name: SkyTempleShotsFired

The event name says shots but it’s really just 1 as far as I can tell.

Contents:

Sky Temple: Temple Captured

Event Name: SkyTempleCaptured

I don’t actually use this one so I forget what’s in it.

Towers of Doom: Altar Captured

Event Name: Altar Captured

Yes there is a space in the event name. Yes it is inconsistent with most of the other event names. This is the event that indicates when a tower gets captured on the Towers of Doom map.

Contents:

Towers of Doom: Six Town Event Start

Event Name: Six Town Event Start

This event indicates that one team owns all six forts on the map. This marks the beginning of such an event. Note that the tracker does not track core shots fired during the six town event.

Contents:

Towers of Doom: Six Town Event End

Event Name: Six Town Event End

This is the matching event for Six Town Event Start. It may sometimes not actually show up in the replay. This may occur due to the game ending before the other team can recapture a fort.

Contents:

Towers of Doom: Fort Captured

Event Name: Town Captured

Emitted when a team captures a fort.

Contents:

Battlefield of Eternity: Immortal Defeated

Event Name: Immortal Defeated

This title is a bit misleading, as it indicates when the immortal fight completes, rather than when the immortal summoned by a team dies.

Contents:

Cursed Hollow: Tribute Collected

Event Name: TributeCollected

Pretty self explanatory here. For some reason the team is stored as a fixed precision int, so do the whole divide by 4096 to resolve. I think location is also in here but I don’t use it, so I forget if that’s true.

Contents:

Dragon Shire: Dragon Knight Activated

Event Name: DragonKnightActivated

It is possible to identify who controlled the Dragon Knight and the Garden Terror, but it is not stored in the event. See the section about determining vehicle control.

Contents:

Garden of Terror: Garden Terror Activated

Event Name: GardenTerrorActivated

It is possible to identify who controlled the Dragon Knight and the Garden Terror, but it is not stored in the event. See the section about determining vehicle control.

Infernal Shrines: Shrine Captured

Event Name: Infernal Shrine Captured

Contents:

Infernal Shrines: Punisher Defeated

Event Name: Punisher Killed

Contents:

Tomb of the Spider Queen: Webweaver Spawn

Event Name: SpidersSpawned

Contents:

Braxis Holdout: Wave Complete

Event Name: BraxisHoldoutMapEventComplete

Contents:

Score (NNet.Replay.Tracker.SScoreResultEvent), _eventid = 11

This event contains the end of game stats (hero damage, xp contribution, etc.).

Contents:

Useful Score IDs

This is a non-exhaustive list. There are a lot of events that start with Wins that I skip because it appears to be used mostly for daily quest tracking. They are not listed in this table.

Score ID Formatted Name Notes
Takedowns Takedowns SoloKill + Assists
Deaths Deaths  
TownKills Town Kills Unclear what a ‘Town Kill’ is
SoloKill Solo Kills This is the value that shows up in the “Kills” column of the in-game stats. It is not a list of kills where no one else assisted.
Assists Assists  
MetaExperience Team Experience Overall Team XP
Level Level  
TeamTakedowns Team Takedowns Total Team Takedowns
ExperienceContribution Experience Contribution  
Healing Healing Usually 0 unless it’s displayed on the scoreboard. Blaze is an exception to this.
SiegeDamage Siege Damage  
StructureDamage Structure Damage Damage specifically to structures
MinionDamage Minion Damage Damage to lane minions
HeroDamage Hero Damage  
MercCampCaptures Merc Camp Captures  
WatchTowerCaptures Watch Tower Captures The “eyes”
SelfHealing Self Healing This is a little weird because it doesn’t count if you use a targeted healing ability (like Malf Q) on yourself.
TimeSpentDead Time Spent Dead In seconds
TimeCCdEnemyHeroes CC Time In seconds
CreepDamage Creep Damage Merc Camp / non-lane minion damage
SummonDamage Summon Damage I think this is damage done to summoned units (like Anub’s beetles) not damage done by summoned units
Tier1Talent Level 1 Talent Tier 1 internal talent ID (int)
Tier2Talent Level 4 Talent Tier 2 internal talent ID (int)
Tier3Talent Level 7 Talent Tier 3 internal talent ID (int)
Tier4Talent Heroic Talent Tier 4 internal talent ID (int)
Tier5Talent Level 13 Talent Tier 5 internal talent ID (int)
Tier6Talent Level 16 Talent Tier 6 internal talent ID (int)
Tier7Talent Storm Talent Tier 7 internal talent ID (int)
DamageTaken Damage Taken This is non-zero only if Blizzard decided to classify the hero as a tank and store the stat.
Role Role Role Stat, doesn’t seem to be used right now?
KilledTreasureGoblin Killed Treasure Goblin Remember the treasure goblin event
GameScore Game Score I actually forget what this is
HighestKillStreak Highest Kill Streak  
TeamLevel Team Level This should be the same as Level
ProtectionGivenToAllies Shielding  
TimeSilencingEnemyHeroes Silence Time In seconds
TimeRootingEnemyHeroes Root Time In seconds
TimeStunningEnemyHeroes Stun Time In seconds
ClutchHealsPerformed Clutch Heals  
EscapesPerformed Escapes Performed Dunno how it defines this
VengeancesPerformed Revenge Kills  
TeamfightEscapesPerformed Team Fight Escapes  
OutnumberedDeaths Deaths While Outnumbered The “do you get ganked a lot and die” stat.
TeamfightHealingDone Team Fight Healing  
TeamfightDamageTaken Team Fight Damage Taken The team fight version of this stat shows up for everyone
TeamfightHeroDamage Team Fight Hero Damage Dealt  
OnFireTimeOnFire Time On Fire  
LunarNewYearSuccesfulArtifactTurnIns Lunar New Year Event Turn Ins  
TimeOnPoint Time On Point  
TimeInTemple Time In Temple  
TimeOnPayload Time on Payload  
CageUnlocksInterrupted Cage Unlocks Interrupted Alterac Pass

Awards

The awards Booleans (the stats that end with Boolean) will be true if the player receieved the named award. false otherwise. These are all false in Custom games.

Score ID Description In-game Award Name
EndOfMatchAwardMVPBoolean MVP MVP
EndOfMatchAwardHighestKillStreakBoolean Highest Kill Streak Award Dominator
EndOfMatchAwardMostVengeancesPerformedBoolean Revenge Kills Award Avenger
EndOfMatchAwardMostDaredevilEscapesBoolean Most Daredevil Escapes Award Daredevil
EndOfMatchAwardMostEscapesBoolean Escape Artist Escape Artist
EndOfMatchAwardMostXPContributionBoolean XP Contribution Award Experienced
EndOfMatchAwardMostHeroDamageDoneBoolean Most Hero Damage Award Painbringer
EndOfMatchAwardMostKillsBoolean Most Kills Award Finisher
EndOfMatchAwardHatTrickBoolean Hat Trick (first 3 kills of the match) Hat Trick
EndOfMatchAwardClutchHealerBoolean Clutch Healer Clutch Healer
EndOfMatchAwardMostProtectionBoolean Most Protection Award Protector
EndOfMatchAward0DeathsBoolean No Deaths Award Sole Survivor
EndOfMatchAwardMostSiegeDamageDoneBoolean Most Siege Damage Award Siege Master
EndOfMatchAwardMostDamageTakenBoolean Most Damage Taken Award Bulwark
EndOfMatchAward0OutnumberedDeathsBoolean No Deaths While Outnumbered Award Team Player
EndOfMatchAwardMostHealingBoolean Most Healing Award Main Healer
EndOfMatchAwardMostStunsBoolean Most Stuns Award Stunner
EndOfMatchAwardMostRootsBoolean Most Roots Award Trapper
EndOfMatchAwardMostSilencesBoolean Most Silences Award Silencer
EndOfMatchAwardMostMercCampsCapturedBoolean Most Merc Camps Award Headhunter
EndOfMatchAwardMapSpecificBoolean Objective Award, appears to be unused right now Map Objective
EndOfMatchAwardMostDragonShrinesCapturedBoolean Most Dragon Shrines Captured Award Shriner
EndOfMatchAwardMostCurseDamageDoneBoolean Most Curse Damage Done Award Master of the Curse
EndOfMatchAwardMostCoinsPaidBoolean Most Coins Paid Award Moneybags
EndOfMatchAwardMostImmortalDamageBoolean Most Immortal Damage Award Immortal Slayer
EndOfMatchAwardMostDamageDoneToZergBoolean Most Damage Done To Zerg Award Zerg Crusher
EndOfMatchAwardMostDamageToPlantsBoolean Most Damage Done To Plants Award Garden Terror
EndOfMatchAwardMostDamageToMinionsBoolean Most Damage Done To Shrine Minions (Infernal Shrines) Guardian Slayer
EndOfMatchAwardMostTimeInTempleBoolean Most Time In Temple Award Temple Master
EndOfMatchAwardMostGemsTurnedInBoolean Most Gems Turned In Award Jeweler
EndOfMatchAwardMostSkullsCollectedBoolean Most Skulls Collected Award Skull Collector
EndOfMatchAwardMostAltarDamageDone Most Altar Damage Done Award (Towers of Doom) Cannoneer
EndOfMatchAwardMostNukeDamageDoneBoolean Most Nuke Damage Done Award Da Bomb
EndOfMatchAwardMostTeamfightDamageTakenBoolean Most Team Fight Damage Taken Award Guardian
EndOfMatchAwardMostTeamfightHealingDoneBoolean Most Team Fight Healing Done Award Combat Medic
EndOfMatchAwardMostTeamfightHeroDamageDoneBoolean Most Team Fight Hero Damage Done Award Scrapper
EndOfMatchAwardGivenToNonwinner End of Match Award Given to Non Winner, internal  
EndOfMatchAwardMostTimePushingBoolean Most Time Pushing Award Pusher
EndOfMatchAwardMostTimeOnPointBoolean Most Time on Point Award Point Guard
EndOfMatchAwardMostInterruptedCageUnlocksBoolean Most Interrupted Cage Unlocks  

Hero Banned (NNet.Replay.Tracker.SHeroBannedEvent), _eventID = 13

I don’t actually use this event because it’s easier to just pull from the attributeevents data, as that’s in a fixed location. But it does exist. I forget the exact contents but the hero name is not in a useful format. It appears to be the internal hero name, which unless you want to manually build that index, is not something that is easily converted to an actual hero name.

Hero Picked (NNet.Replay.Tracker.SHeroPickedEvent), _eventID = 14

Indicates that a player picked a hero. These events will be in draft order, so the first one you see in the trackerevents is the first pick. I only use this event for pick order.

Contents:

Message Events

These consist of pings and text messages sent through the game interface. Team chat is recorded for your team (so no you can’t see the other team’s chat in the replay). Party chat is not included if you were in a party.

Common Contents:

{
  _eventid: [int],
  _event: [string],
  _gameloop: [int],
  _userid: {
    m_userId: [int]
  }
}

All message events have the above contents. The user ID is the Lobby ID as described in the Players section. _eventid will be one of the following and is used to identify what kind of message the event is. _event contains a string representation of the event.

_eventid Name Notes
0 Chat Chat message
1 Ping Ping
2 Loading Progress You can use this to figure out who’s loading the slowest I guess.
3 Server Ping Not sure what this does really.
4 Reconnect Notify Can use to determine who DC’d
5 Player Announce An announcement ping (health, mana, etc.)

Chat, _eventid = 0

A text message sent to an in-game channel.

Contents:

Ping, _eventid = 1

A map ping.

Contents:

Player Announce, _eventid = 5

A player announcment (health, mana, etc.)

Contents:

Announcement Types

Keys are string keys for an object.

Key Value Notes
"None"   Not sure really what this does
"Ability" ? This indicates the cooldown of the selected ability. I think the contents are the ability ID, but haven’t checked recently
"Behavior" ? Unknown
"Vitals" Integer 0 = Health, 2 = Mana, 1 = ???

Game Events

Every action executed by all players in the match. The results of said actions can only be fully resolved by running the game in a game client with the same version as the replay (which is why HotS downloads the required files and launches a different client when viewing older replays).

The only thing I use this for right now is b-step detection, taunts, and dances The game event IDs can change from build to build, so this will be to the best of my knowledge. Use the player Lobby ID to resolve event._userid.m_userId to a player.

Common elements:

{
  _userid: [Player Lobby ID (int)],
  _eventid: int,
}

There are a lot more events in here, but these are mostly very cryptic. Feel free to decompress these events and look manually, but be warned that it’s going to be a ~100MB json file.

Player Back (b)

A b-step is just a series of repeated b commands. My parser tracks these events and as long as the presses are fast enough (within 16 frames), it marks the series of presses as a b-step. m_abil may be null so check that before accessing.

_eventid m_abil.m_abilLink For Build
27 200 <61872, this was around the first time I noticed it break so this period is a little iffy
27 119 >= 61872 I think, if not that build number then definitely >= 63070, < 68740
27 116 >= 68740, < 70682
27 112 >= 70682, < 77525
27 114 Current

Player Taunt

m_abil may be null so check that before accessing.

_eventid m_abil.m_abilLink m_abilCmdIndex For Build
27 19 4 < 68740
27 22 4 Current

Player Dance

m_abil may be null so check that before accessing. I suspect that m_abilCmdIndex relates to the emote wheel, and that indices 1-2 may correspond to sprays, and voice lines. This is unconfimed and also unnecessary, as those events are stored in the tracker events.

_eventid m_abil.m_abilLink m_abilCmdIndex For Build
27 19 3 < 68740
27 22 3 Current

Special Cases for Map Objectives

Sometimes the tracker doesn’t have the data, but other places do.

Braxis Holdout

Blizzard doesn’t tell us what the wave strength actually is at any point in the replay file. But based on the units in the wave we can determine both who won (100%) and the practical percentage of the wave. There are cutoffs where a percent or two doesn’t matter, as it doesn’t end up spawning a new unit. Wave % values for each unit taken from the Heroes of the Storm Gamepedia Wiki.

Determining when the Beacons are Active

As soon as a Braxis wave-specific unit shows up in UnitBorn after the game starts, the beacons are active. Typically these should be two ultralisk units for each team. Astute game observers will note that this is because the ultralisks are physically in cages during the beacon phase. We’ll come back to those units later. This process can be repeated once all of the units spawned for this first wave die.

Determining the Wave Strength

When the beacons are active, Track the UnitBorn events for Braxis Holdout unit types (listed in the table under UnitBorn). The controlling player should be 11 (blue) or 12 (red). This is the team’s AI player. At any point while the beacons are active, the wave strength for each team is computed as shown in the following pseudocode:

function braxisWaveStrength(units) {
  score = 0.1 * types[ReplayTypes.BraxisUnitType.ZergBaneling];
  score = Math.max(
    score,
    0.25 * (types[ReplayTypes.BraxisUnitType.ZergHydralisk] - 2)
  );
  score = Math.max(
    score,
    0.35 * (types[ReplayTypes.BraxisUnitType.ZergGuardian] - 1)
  );
}

For a reference implementation, see the braxisWaveStrength function in the parser. Ultralisks are technically worth 50% each, but they are both present for both teams at all points during the beacon phase, so they’re useless for determining wave strength.

Determining Wave Start

The most reliable way to find the start of a zerg wave that I’ve found, is to see when one team’s ultralisks die. When an ultralisk is killed at the start of a wave, the UnitDied event has a very specific format. If the ultralisk was killed, and m_killerPlayerId = null, then the team controlling that ultralisk has lost the wave. One or both of the ultralisks may be killed, but there will never be two ultralisks on each team, since that’d be a 100-100 wave. Once this event happens, the wave starts. The wave ends when all zerg units on both teams have died. At this point, you can start watching for a new beacon phase again by monitoring the unit spawns for zerg units.

Warhead Junction

Credit to barret777 for figuring this one out.

We can distinguish a successful nuke launch from an unsuccessful one by tracking how long a certain unit has been active. When a nuke is going to drop, the NukeTargetMinimapIconUnit gets spawend, noted in UnitBorn. If this unit is alove for more than 1.5 seconds, the launch is successful. Otherwise, the launch was interrupted. The spawn location of this unit indicates the nuke drop position on the map.

Volskaya Foundry

The objective is complete when the Triglav Protector unit spawns. This unit has the type VolskayaVehicle, and when it dies the next objective can spawn afterwards.

Tracking who’s in this vehicle is a little difficult. UnitOwnerChange events will fire when someone gets in/out of the vehicle. Since there are two parts (technically two units) on it, and players are allowed to exit the vehicle, you’ll have to identify which unit is the pilot and gunner and track that data. I don’t track this as it’s a lot of data of questionable utility.

Determining Vehicle Control

When a single-player vehicle spawns, control can be determined by watching for UnitOwnerChange events after a vehicle spawns. The player ID of that event will be the Tracker Player ID for the player piloting the vehicle.

Haunted Mines Golem Spawns

I haven’t been able to find skull count data for these, but when the UnderworldSummonedBoss units get spawned, the golem phase begins. One of these will spawn for each team, identified by IDs 11 and 12.

Beacon Control (Dragon Shire, Braxis Holdout)

Track the tag and recycle tag of the beacon unit at the start of the game, then track UnitOwnerChange events over the course of the game. When not owned by either team, the ID may be 0. If you’re indicating blue team with 0 and red team with 1, you’ll need a third value to indicate no team for this event.