Script Structure ​
A Lua script is a .lua file in the scripts folder. No fields are strictly required. An empty file is a valid script. Define only what you need.
Your most basic script will probably look something like this:
name = "Aim Assist"
function on_tick()
if is_in_menu() then return end
if not input.is_key_held(VK.XBUTTON2) then return end
local target = targeting.find_closest_by_fov(15, 50)
if target and target:is_alive() then
snap_to_target(target, { bone = bone.chest })
end
endon_tick runs every ~4ms (the rate at which the cheat reads memory) and is what 90%+ of scripts will use as their main loop. It runs as a coroutine, return to start fresh next tick, or call coroutine.yield() to resume from the same line preserving all local state.
Event-only scripts ​
on_tick isn't required. If your script only needs to react to game events, kills, modifiers, ability casts, you can skip it entirely. This is a complete event-only script:
name = "Kill Logger"
function on_kill(player)
toast(player:hero_name() .. " died!", 2)
endThe full list of available event callbacks is in the table below.
Optional globals ​
| Field | Default | Purpose |
|---|---|---|
name | filename (without .lua) | Display name in menu |
id | hero_id.any (all heroes) | Hero filter: hero_id.haze, etc. |
description | "" | Tagline shown in the menu |
settings | none | Config table rendered in the menu |
needs_all_particles | false | Receive all particle events, not just ability-related ones |
Optional callbacks ​
Define only the callbacks you need.
| Callback | When it fires |
|---|---|
on_tick() | Every ~4ms, runs as a coroutine |
is_enabled() | Checked before every tick; return false to skip this tick |
on_kill(player) | A player dies |
on_modifier_added(player, mod) | A modifier is applied to any player |
on_modifier_removed(player, mod) | A modifier is removed from any player |
on_ability_cast(player, info) | An ability (slots 0-3) went on cooldown |
on_item_used(player, info) | An active item (slots 4-7) went on cooldown |
on_particle_create(event) | A particle system spawned |
on_particle_destroy(event) | A particle system destroyed |
Parameter details for each callback are in the Events reference.
is_enabled() is checked every tick before resuming on_tick. If it returns false, the coroutine is paused at its current coroutine.yield(). Not killed. When is_enabled() returns true again, the script resumes from exactly where it left off, with all local state preserved. For long-running helpers like fire_and_hold, this means a mid-hold disable will pause the wait; on re-enable the hold continues, though wall-clock timing keeps advancing in the meantime so the remaining hold window may have partially or fully elapsed.
Reloading ​
Edits to a .lua file are picked up by toggling that script off then back on in the menu, equivalent to a Reload Scripts call but scoped to one script. The Lua state, in-flight coroutines, and custom UI tabs are torn down and rebuilt from scratch. Top-level local variables reset on the cycle; for state that should survive a deliberate toggle, persist it via Storage.
Next steps ​
- Sandbox - which standard libraries are available and which aren't
- Script Settings - declarative settings and the config namespace
- Events - full reference for each callback
- Globals - top-level functions like
clock,toast,print - Player - everything you can ask about a player