Repository navigation
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
| 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.
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.
| 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).
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>").
| 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.
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.
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
Using ProLeak
Features
Tutorial
- Your first plugin
- Plugin basics
- Handling events
- Plugin settings
- User interface
- Players, units and game data
- Purchases, signals and pings
- Chat, camera, input and more
- Cosmetics
- Game modes
- Game updates
- Publishing plugins
Reference