Repository navigation
Chat camera input and more
This page covers the smaller modules of the API, one section each. The API reference lists every function and class.
| Module | For |
|---|---|
chat |
Chat commands and messages |
timers |
Code that runs later or every frame |
tasks |
Work on other threads |
game |
Your slot, raw unit records, the game version |
actions |
Acting as the player |
sfx |
Sounds |
camera |
Moving the camera, first and third person views |
input |
Keys and clicks |
server |
The server kind and its capabilities |
advanced |
Harmony patches and .NET types |
Players, units and game data have their own page: Players, units and game data.
Contents
from proleak import chat
@chat.command("hello")
def hello(cmd):
cmd.reply(f"hi, args={cmd.args}", color="#33ff33")A command stays on this client: teammates never see !hello. cmd.name, cmd.args and
cmd.raw describe what was typed, and cmd.reply(text, color=None) answers in your own chat.
-
send=True(@chat.command("hello", send=True)) lets the message reach the server as typed and still runs the handler. - When two plugins register the same name, the first keeps
!name, and both stay reachable as!<plugin id>:name. -
chat.say(text, color=None)shows a line in your own chat. -
chat.send(text)sends a message as the player and needsactions.
Colors are "#rrggbb". The prefix (! by default) is chat_prefix in Settings.
from proleak import timers
@timers.every(5)
def poll():
...
timers.after(1.5, lambda: print("later"))timers.every(seconds, fn), timers.after(seconds, fn) and timers.on_frame(fn) work as
decorators or calls and return a timer with cancel(). Callbacks run on the main thread.
| Call | Does |
|---|---|
tasks.run_in_thread(fn, *args) |
Runs fn on a worker thread and returns a concurrent.futures.Future. |
tasks.call_soon_main(fn, *args) |
Queues fn for the main thread at the next frame. Works from any thread. |
tasks.spawn(coro) |
Runs a coroutine on a shared asyncio loop. |
Worker threads receive PluginUnloaded when the plugin unloads.
from proleak import chat, tasks
def fetch():
... # slow work, off the main thread
tasks.call_soon_main(chat.say, "done")
tasks.run_in_thread(fetch)| Call | Returns |
|---|---|
game.local_player() |
Your player slot (1 to 8). world.player() gives the Player object. |
game.unit(unit_id) |
{id, type, owner, x, z, alive, placed, wave_fighter}, or None. |
game.units() |
Every unit, as those records. |
game.version() |
The running game's version. |
game.built_for() |
The game version this ProLeak was built for. |
game.available(name) |
True when an event, an action (actions.hire) or a feature (world) runs on this game; otherwise the reason, as a string. |
wave_fighter and placed link a placed fighter and the unit that fights for it in a wave
(see Units); both are -1 for other units.
| Call | Acts |
|---|---|
actions.build(builder_unit, unit_type, x, z) |
Places a fighter at map point (x, z). |
actions.train(unit, unit_type) |
Upgrades a fighter, or trains a unit. |
actions.hire(unit, unit_type) |
Hires a mercenary from your king or barracks. |
actions.upgrade(upgrade_id, level) |
Buys a king upgrade or Upgrade Supply. |
They need actions, so they work in local games only (see Plugin
permissions), and return whether the game accepted the
request; the server may still refuse it.
Note
The game's data calls upgrades researches: upgrade_id is a research id such as
king_attack_research_id. data.king_upgrades() lists the king's.
Sounds play on this client only, on every server kind.
from proleak import sfx
sfx.play_ui("goodjob") # a UI sound by name; sfx.names() lists them
sfx.announce("round-start") # the same sounds, on the announcer's channel
sfx.what(unit) # the line a unit says when selected
sfx.play_from(path, unit) # a sound bank path, coming from a unit
sfx.play_at(path, x, z) # a sound bank path, at a map point
sfx.play_global(path) # a sound bank path, heard everywhereplay_ui and announce refuse a name the game does not have and suggest close ones.
camera.get() returns {x, y, z, yaw, pitch, fov}. camera.set(...),
camera.focus_unit(unit_id), camera.reset(), camera.pov(...) and
camera.third_person(...) need actions.
camera.pov(unit, offset=(0, 0, 0), pitch=0, show_unit=False, look=None)Rides a unit in first person: the camera sits at the unit's head, moved by offset
((right, up, forward) in the unit's own frame), and looks where the unit faces. With
look=(horizontal, vertical) the player can look around by dragging with the middle mouse
button, within those angles in degrees.
from proleak import camera, input, world
view = None
@input.key("V")
def toggle(ev):
global view
if view is not None and view.alive:
view.end()
return
selected = world.player().selected()
if selected:
view = camera.pov(selected[0], offset=(0, 0.2, 0.1))camera.third_person(unit, distance=6, yaw=None, pitch=25, show_unit=True, orbit=True)Follows a unit from distance world units away, looking at its head pitch degrees down;
yaw=None starts behind the unit. With orbit=True a middle-button drag turns the camera
around the unit and the mouse wheel moves it closer or further.
Both return a Pov:
| Member | Meaning |
|---|---|
mode |
"first" or "third". |
offset, pitch, show_unit, look
|
First person; can change while the view lasts. |
yaw, pitch, distance
|
Third person: as the player left them, and can be set. |
alive |
False once the view has ended. |
end() |
Ends the view. |
One plugin holds the view at a time. It ends with view.end(), when its unit leaves the game,
when the plugin unloads, or when the player moves the camera with the minimap or a camera key.
The camera then goes back to where it was.
from proleak import input
@input.key("F8", ctrl=True)
def toggle_overlay(ev):
...
@input.click(button=1)
def right_click(ev):
print(ev.world_x, ev.world_z, ev.unit)| Decorator | Fires |
|---|---|
input.key(name, shift=False, ctrl=False, alt=False) |
For a Unity KeyCode name with exactly the given modifiers, also while the chat box has focus. |
input.click(button=0) |
For a click of that mouse button (0 left, 1 right); ev has the screen and map positions (ev.world_x, ev.world_z). |
input.hover(fn) |
When the cursor moves onto a unit. |
ev.unit is a world Unit, or None when the cursor is over no unit. Handlers
observe; the game still handles the input.
server.kind is menu, official_ranked, official_casual, local or unknown.
server.allows(capability) and server.require(capability) check a capability. Server
permissions describes both.
Warning
Names in both calls follow the game's code and change between game versions.
advanced.patch("Namespace.Type:Method", pre=None, post=None) installs a Harmony patch with
Python callbacks: pre(instance, args) may return False to skip the original, and
post(instance, args, result) sees the return value. It needs actions, at install and at every
call, so it works in local games only, and it refuses the types of ProLeak and its mod loader.
advanced.type("Assets.Api.UnitApi") returns a .NET class to call directly and works on local
servers only.
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
- Game assets
- Game modes
- Game updates
- Publishing plugins
Reference