Skip to content

Players units and game data

Alexandre 'kidev' Poumaroux edited this page Oct 21, 2026 · 3 revisions

Players, units and game data

proleak.world reads the match as it stands: players, teams, units, the wave and the phase. proleak.data reads the game's own data tables: unit types, legions, waves, mercenaries, legion spells, Mastermind playstyles and king upgrades. Both are read-only and work on every server kind. A plugin changes the game only through actions, as a player can.

from proleak import log, on, world

@on("wave.ended")
def report(ev):
    you = world.player()
    log.info(f"wave {ev.wave} done: {len(you.fighters())} fighters, king at {you.team.king.hp:.0f}")

Contents

Reading the world

Call Returns
world.player() the Player this client controls, or None outside a game
world.player(slot) the player in slot (1 to 8), or None
world.players() every playing player (spectators left out)
world.teams() every Team
world.units(**filters) every Unit matching the filters
world.unit(unit_id) the unit with this id now, or None
world.wave the current wave number (0 before wave 1)
world.wave_info the WaveInfo of the current wave, or None before wave 1
world.phase "build", "fight" or "none"

Outside a game the lists are empty and the lookups return None. Like the other game APIs, these work on the main thread only. Reading many members in one handler is cheap: ProLeak reads the world once per frame.

Players and teams

Player:

Member Meaning
slot, name, is_local slot number, display name, True for the player this client controls
team, teammates the player's Team, the other players of that team
attacking, defending the player this one sends mercenaries to, the player sending to this one
gold, mythium, income, workers, food, food_cap, value economy now
connected, afk connection state
legion LegionInfo of the chosen legion
mastermind MastermindInfo of the playstyle, or None
spell SpellInfo of the chosen legion spell, or None
rolls [UnitInfo] the player can build (Mastermind roll)
king_upgrades {"attack": n, "regen": n, "spell": n}, the levels bought
lane the player's lane as an Area
units(**filters) the units the player owns
fighters(**filters) the placed fighters, as units(kind="fighter", ...)
workers_units() the worker units
mercenaries_sent(wave=None), mercenaries_received(wave=None) [UnitInfo] for one wave, or for all
leaked(wave=None) leaked percent for a wave (the current one by default)
selected() the units you have selected; empty for other players, whose selection the client never receives

A Player compares equal to its slot number and converts with int(), so ev.player == 3 and world.player(3) == 3 both hold.

Team has id, players, king (a Unit) and leaked(wave=None), the sum of its players' leak percent.

A leak percent is the bounty that reached the king as a share of the wave's bounty. Leaked mercenaries count too, so it can pass 100.

Units

Member Meaning
id, type game id, unit type id (peewee_unit_id)
hidden True for a placed fighter while its wave fighter fights
kind fighter, wave_fighter, worker, king, mercenary, wave or other
info the UnitInfo of the unit's type
name, legion, tier, role, attack_type, armor_type, evolves_to read from info
owner the owning Player, or None
hp, max_hp, mana, max_mana, x, z, alive state now
upgrades the unit's upgrade ids
researches the game's ids (research ids) of the upgrades that apply to the unit, such as the king upgrades
buffs {buff_type: stacks}
abilities abilities added to the unit, legion spells among them
spell the SpellInfo of the legion spell placed on this fighter, or None
target, targeted_by the unit it attacks (or None), the units attacking it
placed, wave_fighter for a wave fighter, the placed fighter it fights for; for a placed fighter, the unit that fights for it in a wave; else None
area the lane the unit stands in, or None
valid False once the unit died or its id went to another unit

A placed fighter (kind == "fighter") stays on its tile, hidden, during a wave and sends a separate unit (kind == "wave_fighter") to fight. unit.wave_fighter and unit.placed link the two. The placed fighter keeps its handle through the wave, and through an upgrade or a downgrade.

A Unit keeps naming the unit it was created for. Once that unit dies, or the game gives its id to a new unit, valid is False and every other member raises proleak.errors.UnitGone. An upgrade or a downgrade keeps the unit: its type changes and the handle stays valid. A Unit compares equal to its id and hashes by it, so it works as a dictionary key.

An Area has name, owner, contains(x, z) and units(**filters).

Filters

world.units, Player.units, Player.fighters and Area.units take the same keywords, all optional and combined with "and":

Filter Keeps
kind units of that kind
type units of that type id
name units whose display name or type id matches
legion units of that legion, by id or name ("element", "Element")
tier, role, attack_type, armor_type units with that value in their UnitInfo
spell units carrying that legion spell, by id or name
buff units carrying that buff type
owner units of that Player or slot
wave=True wave units, the same as kind="wave" (mercenaries have their own kind)
near=(x, z, radius) units within radius of the point
where units for which the callable returns True

Name comparisons ignore case.

To find the units an aura touches, look for its buff: world.units(buff="<buff type>").

Game data

Call Returns
data.unit(type), data.units(**filters) UnitInfo; units takes kind, name, legion, tier, role, attack_type, armor_type, where
data.legion(id_or_name), data.legions() LegionInfo
data.wave(number), data.waves() WaveInfo
data.mercenaries() the mercenaries a player can send, cheapest first
data.spell(id_or_name), data.spells() SpellInfo (legion spells)
data.mastermind(id_or_name), data.masterminds() MastermindInfo
data.king_upgrades() the three king upgrades: attack, regen, spell
Record Fields
UnitInfo id, name, legion, tier, role, unit_class, gold_cost, mythium_cost, food_cost, value, income, attack_type, armor_type, abilities, evolves_to, evolves_from
LegionInfo id, name, playable, fighters()
WaveInfo number, name, unit (a UnitInfo), count, recommended_value
SpellInfo id, name, description, category, targets_fighter
MastermindInfo id, name, description
KingUpgradeInfo id, research, name

unit_class is fighter, mercenary, wave, worker, king or other. A spell with targets_fighter set goes on one fighter; the others apply to the player.

The values come from the tables the game ships, so a balance patch changes them without a ProLeak release. Early in the main menu, before the game has loaded its tables, lookups return None or empty lists.

Events with objects

Event fields that name a player, unit, team, legion spell or playstyle arrive as objects: ev.player is a Player, ev.unit, ev.placed and ev.king are Units, ev.team is a Team, ev.spell is a SpellInfo and ev.mastermind a MastermindInfo. The events reference notes each such field; other fields stay plain values. ev.info is the data record of the event's first unit type or wave field (for unit.upgraded, the old type). A unit that has already left the world is still a handle with its id, with valid False.

ProLeak raises these events by comparing the world from one frame to the next, after the game has acted, so they have no pre phase:

Event Fields
wave.ended wave
phase.changed phase, wave
unit.upgraded player, unit, old_type, new_type
team.leaked team, wave, percent, players
king.damaged king, team, amount, hp, max_hp
king.died king, team
mercenary.sent player, target, type, wave
spell.chosen player, spell
spell.placed player, spell, unit (the fighter that now carries it)
spell.removed player, spell, unit (the fighter that carried it, when the spell is undone)
unit.downgraded player, unit, old_type, new_type
mastermind.chosen player, mastermind
roll.rerolled player, old, new
king.upgraded player, team, upgrade (attack, regen or spell), level
player.disconnected, player.reconnected, player.afk player

mercenary.sent fires when a wave starts; mercenary.hired fires the moment a player hires one.

unit.sold (player, unit, type) fires when a placed fighter of yours or a teammate's leaves the grid, sold or removed by the game. The client cannot tell an enemy's sale from its lane going out of view, so enemy fighters are left out. fighter.died (unit, placed, player, wave) fires when a wave fighter dies, with the placed fighter it fought for.

Examples

Your Element fighters, and your Peewees:

you = world.player()
element = you.fighters(legion="Element")
peewees = you.fighters(name="Peewee")

The fighter carrying your legion spell:

@on("spell.placed")
def placed(ev):
    if ev.player.is_local:
        log.info(f"{ev.spell.name} is on {ev.unit.name}")

A warning when your team leaks:

@on("team.leaked")
def leak(ev):
    if ev.team == world.player().team:
        ui.toast(f"Leaked {ev.percent:.0f}% on wave {ev.wave}", header="Leak")

Wounded fighters in the current fight:

hurt = world.units(kind="wave_fighter", owner=world.player(),
                   where=lambda u: u.hp < u.max_hp / 2)

The next waves and their units:

for w in data.waves()[world.wave:world.wave + 3]:
    log.info(f"{w.number}: {w.name}, {w.count} x {w.unit.name}")

Previous: User interface | Next: Purchases, signals and pings

Clone this wiki locally