Skip to content

Objectives Extractor

Tower kills, barracks destructions, Roshan kills, and Tormentor kills.

Tormentor Kills

Tracks destruction of Tormentor minibosses. Killer player attribution is resolved by combining combat log death data with the corresponding miniboss kill chat event.

gem.extractors.objectives

Objective event extractor for Dota 2 replays.

Listens to combat log DEATH events and emits structured records for tower kills, Roshan kills, and barracks destructions.

Reference: refs/parser/src/main/java/opendota/Parse.java

TowerKill dataclass

One tower destruction event.

Attributes:

Name Type Description
tick int

Game tick when the tower was destroyed.

team int

Team that owned the tower (2=Radiant, 3=Dire).

killer str

NPC name of the unit that landed the killing blow.

tower_name str

Internal NPC name of the destroyed tower.

Source code in src/gem/extractors/objectives.py
@dataclass
class TowerKill:
    """One tower destruction event.

    Attributes:
        tick: Game tick when the tower was destroyed.
        team: Team that *owned* the tower (2=Radiant, 3=Dire).
        killer: NPC name of the unit that landed the killing blow.
        tower_name: Internal NPC name of the destroyed tower.
    """

    tick: int
    team: int
    killer: str
    tower_name: str

RoshanKill dataclass

One confirmed Roshan death.

Attributes:

Name Type Description
tick int

Game tick of the kill.

killer str

NPC name of the unit that landed the killing blow.

kill_number int

Sequential kill number (1-indexed) for this game.

Source code in src/gem/extractors/objectives.py
@dataclass
class RoshanKill:
    """One confirmed Roshan death.

    Attributes:
        tick: Game tick of the kill.
        killer: NPC name of the unit that landed the killing blow.
        kill_number: Sequential kill number (1-indexed) for this game.
    """

    tick: int
    killer: str
    kill_number: int

BarracksKill dataclass

One barracks destruction event.

Attributes:

Name Type Description
tick int

Game tick when the barracks was destroyed.

team int

Team that owned the barracks (2=Radiant, 3=Dire).

killer str

NPC name of the unit that landed the killing blow.

barracks_name str

Internal NPC name of the destroyed barracks.

Source code in src/gem/extractors/objectives.py
@dataclass
class BarracksKill:
    """One barracks destruction event.

    Attributes:
        tick: Game tick when the barracks was destroyed.
        team: Team that *owned* the barracks (2=Radiant, 3=Dire).
        killer: NPC name of the unit that landed the killing blow.
        barracks_name: Internal NPC name of the destroyed barracks.
    """

    tick: int
    team: int
    killer: str
    barracks_name: str

TormentorKill dataclass

One Tormentor (miniboss) kill event.

Attributes:

Name Type Description
tick int

Game tick of the kill.

killer str

NPC name of the unit that landed the killing blow, or empty string if unknown.

killer_player_id int

Player slot (0–9) of the killing player from the CHAT_MESSAGE_MINIBOSS_KILL event, or -1 if unavailable.

kill_number int

Sequential kill number (1-indexed) for this game.

Source code in src/gem/extractors/objectives.py
@dataclass
class TormentorKill:
    """One Tormentor (miniboss) kill event.

    Attributes:
        tick: Game tick of the kill.
        killer: NPC name of the unit that landed the killing blow,
            or empty string if unknown.
        killer_player_id: Player slot (0–9) of the killing player from the
            ``CHAT_MESSAGE_MINIBOSS_KILL`` event, or ``-1`` if unavailable.
        kill_number: Sequential kill number (1-indexed) for this game.
    """

    tick: int
    killer: str
    killer_player_id: int
    kill_number: int

ShrineKill dataclass

One Shrine of Wisdom destruction event.

Attributes:

Name Type Description
tick int

Game tick of the event.

team int

Team that owned the destroyed shrine (2=Radiant, 3=Dire).

Source code in src/gem/extractors/objectives.py
@dataclass
class ShrineKill:
    """One Shrine of Wisdom destruction event.

    Attributes:
        tick: Game tick of the event.
        team: Team that *owned* the destroyed shrine (2=Radiant, 3=Dire).
    """

    tick: int
    team: int

AegisEvent dataclass

An Aegis of the Immortal pickup, steal, or denial event.

Attributes:

Name Type Description
tick int

Game tick of the event.

player_id int

Player slot (0–9) who picked up / stole / denied the Aegis. -1 if the event had no player attribution.

event_type str

"pickup", "stolen", or "denied".

Source code in src/gem/extractors/objectives.py
@dataclass
class AegisEvent:
    """An Aegis of the Immortal pickup, steal, or denial event.

    Attributes:
        tick: Game tick of the event.
        player_id: Player slot (0–9) who picked up / stole / denied the Aegis.
            -1 if the event had no player attribution.
        event_type: ``"pickup"``, ``"stolen"``, or ``"denied"``.
    """

    tick: int
    player_id: int
    event_type: str

ObjectivesExtractor

Extracts tower kills, Roshan kills, barracks kills, tormentor kills, and shrine kills from a replay.

Attach to a ReplayParser before calling parse():

Example

extractor = ObjectivesExtractor() extractor.attach(parser) parser.parse() print(extractor.roshan_kills)

Attributes:

Name Type Description
tower_kills list[TowerKill]

All tower kill events in chronological order.

roshan_kills list[RoshanKill]

All Roshan kill events in chronological order.

barracks_kills list[BarracksKill]

All barracks kill events in chronological order.

aegis_events list[AegisEvent]

All Aegis pickup / steal / denial events.

tormentor_kills list[TormentorKill]

All Tormentor (miniboss) kill events in chronological order.

shrine_kills list[ShrineKill]

All Shrine of Wisdom destruction events in chronological order.

Source code in src/gem/extractors/objectives.py
class ObjectivesExtractor:
    """Extracts tower kills, Roshan kills, barracks kills, tormentor kills, and shrine kills from a replay.

    Attach to a ``ReplayParser`` before calling ``parse()``:

    Example:
        >>> extractor = ObjectivesExtractor()
        >>> extractor.attach(parser)
        >>> parser.parse()
        >>> print(extractor.roshan_kills)

    Attributes:
        tower_kills: All tower kill events in chronological order.
        roshan_kills: All Roshan kill events in chronological order.
        barracks_kills: All barracks kill events in chronological order.
        aegis_events: All Aegis pickup / steal / denial events.
        tormentor_kills: All Tormentor (miniboss) kill events in chronological order.
        shrine_kills: All Shrine of Wisdom destruction events in chronological order.
    """

    tower_kills: list[TowerKill]
    roshan_kills: list[RoshanKill]
    barracks_kills: list[BarracksKill]
    aegis_events: list[AegisEvent]
    tormentor_kills: list[TormentorKill]
    shrine_kills: list[ShrineKill]

    def __init__(self) -> None:
        self.tower_kills = []
        self.roshan_kills = []
        self.barracks_kills = []
        self.aegis_events = []
        self.tormentor_kills = []
        self.shrine_kills = []

    def attach(self, parser: ReplayParser) -> None:
        """Register this extractor's callbacks with a parser.

        Args:
            parser: The ``ReplayParser`` instance to attach to.
        """
        parser.on_combat_log_entry(self._on_combat_log)
        parser.on_chat_event(self._on_chat_event)

    def _on_chat_event(self, msg: Any, tick: int) -> None:
        event_type = _AEGIS_EVENT_TYPE.get(msg.type)
        if event_type is not None:
            self.aegis_events.append(
                AegisEvent(tick=tick, player_id=msg.playerid_1, event_type=event_type)
            )
        elif msg.type == _CHAT_MSG_SHRINE_KILLED:
            # value = team that owned the shrine (2=Radiant, 3=Dire)
            self.shrine_kills.append(ShrineKill(tick=tick, team=msg.value or 0))
        elif msg.type == _CHAT_MSG_MINIBOSS_KILL and self.tormentor_kills:
            # playerid_1 = player slot of the killer. Patch the most recently
            # recorded tormentor kill (from the DEATH combat log event) with
            # the player slot attribution from this chat event.
            self.tormentor_kills[-1].killer_player_id = msg.playerid_1

    def _on_combat_log(self, entry: CombatLogEntry) -> None:
        if entry.log_type != "DEATH":
            return
        target = entry.target_name
        if target == "npc_dota_roshan":
            self.roshan_kills.append(
                RoshanKill(
                    tick=entry.tick,
                    killer=entry.attacker_name,
                    kill_number=len(self.roshan_kills) + 1,
                )
            )
        elif target == "npc_dota_miniboss":
            self.tormentor_kills.append(
                TormentorKill(
                    tick=entry.tick,
                    killer=entry.attacker_name,
                    killer_player_id=-1,  # resolved from chat event if available
                    kill_number=len(self.tormentor_kills) + 1,
                )
            )
        elif target.startswith("npc_dota_goodguys_tower") or target.startswith(
            "npc_dota_badguys_tower"
        ):
            self.tower_kills.append(
                TowerKill(
                    tick=entry.tick,
                    team=_find_team(target, _TOWER_TEAM),
                    killer=entry.attacker_name,
                    tower_name=target,
                )
            )
        elif (
            target.startswith("npc_dota_goodguys_melee_rax")
            or target.startswith("npc_dota_goodguys_range_rax")
            or target.startswith("npc_dota_badguys_melee_rax")
            or target.startswith("npc_dota_badguys_range_rax")
        ):
            self.barracks_kills.append(
                BarracksKill(
                    tick=entry.tick,
                    team=_find_team(target, _BARRACKS_TEAM),
                    killer=entry.attacker_name,
                    barracks_name=target,
                )
            )

attach(parser: ReplayParser) -> None

Register this extractor's callbacks with a parser.

Parameters:

Name Type Description Default
parser ReplayParser

The ReplayParser instance to attach to.

required
Source code in src/gem/extractors/objectives.py
def attach(self, parser: ReplayParser) -> None:
    """Register this extractor's callbacks with a parser.

    Args:
        parser: The ``ReplayParser`` instance to attach to.
    """
    parser.on_combat_log_entry(self._on_combat_log)
    parser.on_chat_event(self._on_chat_event)