Events, menu elements, drawing, HUD windows, game data, files, network and memory: one API in every product. Everything on this page is the live reference, with an example you can paste as is.
modules
26
functions and fields
210
games documented
10
Getting started
01
Write it
A script is one .lua file. Every example below is a complete v2 script: copy it, or start from the one below.
02
Load it
Open the Lua tab of the in-game menu, or write it in the Editor tab. Signed in on the panel, Run in game sends a script straight to your loader.
03
Iterate
script.reload() picks up your edits. An error stays in the console with its line: it never takes the overlay or the other scripts down.
first_script.lua
local key = ui.create("Demo"):group("Keys"):hotkey("Aim key", 0x02, "hold")
events.render:set(function()
if key:get() then render.text(nil, vector(20, 60), color(255, 200, 0), "s", "aiming") endend)
How scripting works
A short mental model before the reference.
01
Events, not entry points
There is no OnDraw / OnMenu / OnThread any more. A script subscribes to events: events.render:set(fn) draws every frame, events.update:set(fn) runs before drawing, events.key_down, events.esp_player… Several callbacks per event, :unset(fn) removes one.
02
Menu elements are objects, made once
ui.create("My script") gives a tab, tab:group("Aimbot") a box, group:checkbox("Enabled", false) an element. Each one is a real menu setting: saved in configs, editable on /live, with :get(), :set(), :set_callback(), :depend()… No per-frame rebuilding.
03
vector and color everywhere
Positions and sizes are vectors (vector(x, y, z)), colors are colors (color(r, g, b, a)). render.rect(pos, size, col) - never packed integers or x, y, w, h lists.
04
The render thread never waits
Long calls (network, memory scans, device connects, os.execute…) take a callback as their last argument in a script and return at once. In a thread.spawn worker, call them without a callback: they block there, never the overlay.
05
One state per script, permissions per script
Each script has its own Lua state, memory cap (256 MB by default) and time budget (1 s per callback). Network, files outside its data folder, memory, devices, os and configs need a --@permissions header and the user's approval once; "Allow unsafe scripts" stays the master switch.
06
Everything is cleaned up on unload
Callbacks, timers, threads, menu elements, fonts, images, HUD windows, pending async calls and memory hooks belong to the script that made them and go away when it unloads. No callback of an unloaded script ever runs.
Per game
10 games
The API is the same in every product. What changes is the data: the players entity.* lists, the ESP elements, the hitbox names and the get_prop fields. A script can read elusive.product and game.name() to adapt.
Nothing matches.
Try a module name (render, ui, memory) or an old v1 name: the migration table maps it to the new one.
What a script knows about itself and the runtime. elusive.api_version is 2: no breaking change without a new number.
Module 02
events
11 entries
The entry points of a script. Each event object has :set(fn), :unset(fn); several callbacks per event run in the order they were set, each under its own script's time budget. Engine events cannot be fired by a script; events.custom(name) is the channel between scripts.
Module 03
timer
3 entries
Deferred and repeated callbacks on the render thread. At most 256 timers per script; a timer that runs out of its budget or raises 64 times in a row is cancelled.
Module 04
thread
6 entries
Background work in its own Lua state on its own OS thread. The function is self-contained: it sees none of the script's locals or globals (pass them as arguments; a function that captures a local is refused). Values cross as copies, like events.custom. At most 16 threads per script, 256 MB each; a loop that never calls thread.sleep / thread.recv is stopped after 300 million instructions; threads stop when the script unloads. A finished thread stays readable (recv) while it has unread messages, the last 16 of them only. A thread has its script's permissions; turning "Allow unsafe scripts" off or on reaches the running threads at once.
Module 05
ui
menu20 entries
The in-game menu. An element is made once (at the top level of the script) and becomes a real menu setting: drawn like the cheat's own under the Scripts category, saved in the configs, shown and editable on /live, listed in the keybinds HUD and the search. Stable ids: scripts.<script>.<tab>.<group>.<label>. Per script: 8 tabs, 64 groups, 1024 elements. Not available in a thread.
Module 06
vector
5 entries
The position / size / direction type. vector(x, y) is 2D (z = 0), vector(x, y, z) 3D. Fields x, y, z are writable. Operators: + - (vectors), * / (a number or a vector), unary -, ==, tostring.
Module 07
color
3 entries
The color type, 0..255 per channel. Fields r, g, b, a are writable (clamped when drawn). == and tostring work.
Module 08
render
events.render15 entries
Drawing over the game. The drawing calls work inside events.render and events.esp_player only; measure_text, screen_size, world_to_screen, load_font and load_image work anywhere in the script. Positions and sizes are vectors, colors are colors. At most 100,000 shapes per script per frame; a NaN or infinite coordinate draws nothing.
Module 09
hud
menu3 entries
The script's own HUD windows, in the style of the keybinds / spectators lists: draggable while the menu is open, magnetic, their placement saved with the configs. 16 windows per script; 256 items per window; not available in a thread.
Module 10
game · entity
varies by game11 entries
The game data, from the snapshot the product already fills every frame: these calls never read the game themselves. What each game exposes (players listed, ESP elements, hitbox names, get_prop fields, entity kinds, world axes) is in the "Per game" section above; a value a game does not have is nil. Not available in a thread.
Module 11
esp
varies by game10 entries
Take part in the cheat's own player ESP: hide players, recolor them, rewrite their labels, add texts, bars and flags laid out with the ESP's own elements (same sides, same stacking, same order as in the ESP preview). events.esp_player gives one (player, e) per player the ESP draws; esp.flag adds a real ESP element. Nothing is read from the game for this. Not available in a thread.
Module 12
input
4 entries
The game PC's keyboard and mouse, as the cheat reads them. Works in threads too. A vk outside 1..255 raises.
Module 13
common · configs
5 entries
Notifications, host information, and the configs (the cheat's saved settings). common works in threads; configs does not.
Module 14
radar
3 entries
The web radar (the Webradar tab: a live map of the game at elusive.is/r/<code>): its state, and marks your script puts on the viewers' map. Scripts only, not threads.
Module 15
string · table · math · bit · random
8 entries
Lua 5.4's standard library (base, coroutine, table, string, math, utf8, the safe part of os) with the functions that could loop in C replaced by bounded ones, plus these additions. Available in scripts and threads. Removed on every state: io, debug, package, dofile, loadfile, string.dump, bytecode loading (load takes text only), os.exit / remove / rename / tmpname / setlocale, warn, and __gc in setmetatable.
Module 16
json · base64 · crypto · regex
7 entries
Data libraries, available in scripts and threads, no permission needed.
Module 17
files
files permission (outside the data folder)5 entries
The script's files. A relative path is inside the script's own data folder, <scripts>\data\<slug>\ (made on first use; files.data_dir() gives it): no permission needed. Any other path (absolute, .., a drive) needs the files permission. Device names (CON, NUL, \\.\PhysicalDrive0…) are always refused. Works in threads too.
Module 18
network
network permission2 entries
HTTP and HTTPS requests. In a script they take a callback as the last argument and return at once; in a thread, call them without a callback and they return res | nil, err. 10 redirects, 64 MB body at most.
Module 19
clipboard · os
clipboard / os permissions3 entries
The host's clipboard and shell. os.time, os.clock, os.date and os.difftime are always there; os.getenv and os.execute need the os permission.
Module 20
memory
memory permission11 entries
Direct reads and writes in the game's memory over the DMA link. Prefer entity.*: it is read once per frame in one scatter, while every call here goes to the device. Quick calls answer at once; the long ones (scans, listings) take a callback in a script and block in a thread. Sizes per call: 1 MB in a script, 16 MB in a thread.
Module 21
shellcode
memory permission4 entries
Code hooks in the game. Every patch saves the bytes it overwrites and they are written back when the script unloads (unload, reload, shutdown), newest first, so a reload hooks a clean game. A range another script patched is refused; a hook whose write fails is undone.
Module 22
serial
device permission5 entries
MAKCU, KMBox B, Ferrum… over USB serial, on the same link as the Device settings. The long calls (open, connect, query) take a callback in a script and block in a thread. While a connect runs, the quick calls answer false / nil, "busy: the device is connecting" instead of blocking the overlay.
Module 23
kmbox
device permission7 entries
KMBox Net over UDP. In a script, commands are queued and sent in order by the device's own thread: a call returns true at once (or false, err when there is no box or 256 commands are waiting). In a thread they wait for the box. The release of a click / key, and key_up / button(b, false) / release_keys, are always sent, even if the script unloads in between.
Module 24
permissions · header
8 entries
What a script declares in its first 64 lines. "Allow unsafe scripts" (Configs & scripts > Lua) gives every script the permissions it declares, nothing to allow per script; when it is off, no permission applies. A call without its permission raises a message that says what to declare, or to turn the switch on.
Module 25
limits
8 entries
What keeps one script from taking the overlay or the other scripts down. Over a limit, the call raises a clean Lua error (or the script is unloaded with a clear message); nothing crashes.
Module 26
migrating from v1
old → new33 entries
API v2 is a clean break: OnDraw, OnMenu, OnThread and the immediate-mode menu are gone. Search the old name here to find its replacement. Behaviour that changed too: errors in a callback no longer stop other scripts; blocking calls (HTTP, serial queries, device connects, memory scans, os.execute) take a callback; memory hooks are undone when the script unloads; "Allow unsafe scripts" now gives a script only the permissions it declares (--@permissions).
the ESP's weapon label (the hero's name for the generic weapon); get_prop("hero") is always the hero
entity.get_entities()
"trooper", "neutral", "boss": the NPCs (type, health, max_health, team 2 Amber / 3 Sapphire, alive, enemy, designer_name, model); your team's and dead ones included
ESP elements esp.elements()
health
weapon
ammo
flags
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
pelvis
upper_arm_l
upper_arm_r
lower_arm_l
lower_arm_r
hand_l
hand_r
upper_leg_l
upper_leg_r
lower_leg_l
lower_leg_r
ankle_l
ankle_r
Fields p:get_prop(name), any other name gives nil
Hero (synthesised)
hero
ultimate_ready
ultimate_cooldown
Progression
m_iLevel
m_iGoldNetWorth
m_iPlayerKills
m_iPlayerAssists
m_iDeaths
m_nAssignedLane
m_nHeroID
m_bUltimateTrained
Clip & aim
m_iClip
m_iClipSize
m_iBonusClip
m_iMaxAmmo
m_flAmmoFrac
m_bIsZoomed
m_bInReload
m_flNextPrimaryAttack
m_angEyeAngles
Match
m_iConnected
m_lifeState
m_iTeamNum
m_steamID
m_unLobbyPlayerSlot
m_bAlive
m_isVisible
Ids & names
m_iszPlayerName
m_szName
m_modelName
m_flSimulationTime
m_flUltimateCooldownStart
m_flUltimateCooldownEnd
Position
m_vecOrigin
m_vecAbsVelocity
m_vecViewOffset
No armor. The "flags" element is the ESP's flag column (ULT, LV, RELOAD, ZOOM…).
ultimate_cooldown is the seconds left (0 when ready).
Example for Deadlock
local box = ui.create("Deadlock Demo", "Users"):group("Teammates")
local on = box:checkbox("Lane + ult cooldown", true)
events.esp_player:set(function(p, e)
ifnot on:get() or p:is_enemy() thenreturnendlocal lane = p:get_prop("m_nAssignedLane")
if lane then e:add_text("top", "L" .. lane, color(120, 220, 160)) endif p:get_prop("ultimate_ready") then
e:add_text("bottom", "ULT READY", color(255, 140, 0))
elselocal cd = p:get_prop("ultimate_cooldown") or0if cd > 0then e:add_text("bottom", string.format("ult %ds", math.ceil(cd)), color(200, 200, 200)) endendend)
m_flUltCharge is 0..100, nil for a training bot. get_team() is nil when unknown.
Hitboxes use the CS2 layout, nil while the hero is dead.
Example for Overwatch 2
local box = ui.create("OW Demo", "Zap"):group("Ult warning")
local on = box:checkbox("On", true)
local at = box:slider("Threshold", 50, 100, 85, { unit = "%" })
esp.flag("ULT", color(255, 140, 0), function(p)
local u = p:get_prop("m_flUltCharge")
return on:get() and p:is_enemy() and u ~= niland u >= at:get()
end)
events.esp_player:set(function(p, e)
ifnot p:is_enemy() ornot p:is_alive() thenreturnend
e:add_text("bottom", p:get_prop("m_szHeroName") or"?", color(255, 255, 255))
local head = p:get_hitbox_position("head")
local s = head and head:to_screen()
if s thenlocal top = (head + vector(0, 0.15, 0)):to_screen() -- Y is up in OWlocal r = top and math.clamp((s - top):length(), 2, 40) or6
render.circle(s, r, color(255, 255, 255, 220), 1.5, 24)
endend)
the raiders of the game state (ARC machines are entities, not players)
World axes
Z up, meters
is_enemy()
not local and not in the local player's squad
get_weapon()
the held item: the game's English name, weapons upper case with their tier ("RATTLER III")
get_health()
0 while downed (m_bDowned, m_flDownedSeconds)
entity.get_entities()
"machine" (threat, velocity, weak_points + weak_point_1..8, target "you" / "teammate" / "other", alertness, combat_phase), "projectile" (grenades, missiles, ARC shells: velocity, impact, friendly, arc, radius), "loot" (category container / plant / item / cargo / activity, opened, tier, value, rarity 0 Common .. 4 Legendary, 5 ascension module, asset_id, item_category as in entity.get_catalog(), note "breaching" / "dropped"), "trap" (friendly, radius in m), "extraction" (hatch, state, can_extract, enabled, state_seconds, shutdown_at_raid_left), "quest" (objective markers), "ping" (the squad's pings while they last: name = the game's words, pinger, mine), "place" (the map's named places and threat zones: name in the game's language, english, region, size "major" / "medium" / "minor", threat 0 none, 2 high, 3 extreme), "raid" (time_left, event = the raid's event when there is one, raiders, up, downed, extracted, dead, squads)
entity.get_catalog()
every item of the game by its English name (weapons once per tier: "Rattler III"), category "Weapons", "Weapon mods", "Shields", "Augments", "Consumables", "Deployables", "Gadgets", "Grenades", "Materials", "Quest items", "Blueprints", "Ascended upgrades" or "Other"
ESP elements esp.elements()
health
shield
downed
status
firing
action
talking
aiming
ping
squad
weapon
gear
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
pelvis
upper_arm_l
upper_arm_r
lower_arm_l
lower_arm_r
hand_l
hand_r
upper_leg_l
upper_leg_r
lower_leg_l
lower_leg_r
ankle_l
ankle_r
Fields p:get_prop(name), any other name gives nil
Pool
m_iHealth
m_iMaxHealth
m_iShield
m_iMaxShield
m_iShieldType
State
m_bAlive
m_bDowned
m_status
m_flDownedRatio
m_flDownedSeconds
m_bTeammate
m_bIsLocalPlayerController
m_isVisible
m_hasBones
Actions
m_bADS
m_bReloading
m_flReloadLeft
m_bUsingItem
m_szUsingItem
m_bInteracting
m_bSprinting
m_bTalking
m_flAimYaw
m_flAimPitch
m_flActorYaw
m_bAimingAtYou
Flags & held item
m_bFiring
m_iPing
m_iSquad
m_szWeaponName
m_iWeaponLevel
m_iAmmo
Gear (when the game sends it)
m_szStowedWeapons
m_szArmor
m_szAugment
Projectiles (local player)
m_flBulletSpeed
m_flBulletGravityScale
Name & position
m_iszPlayerName
m_vecOrigin
m_vecAbsVelocity
m_vecViewOffset
m_status: 0 alive, 1 downed, 2 crabbed, 3 dead, 4 defeated. Shield props are nil without a shield item; m_iShieldType 0 Light, 1 Medium, 2 Heavy.
Actions are the raider's animation state, read every 50 ms: nil for the local player and while unknown (far away). m_bAimingAtYou: his aim ray passes within about a meter of your chest.
Hitboxes only when the skeleton was read (m_hasBones).
Example for Arc Raiders
esp.flag("ON YOU", color(255, 40, 40), function(p)
return p:is_enemy() and p:get_prop("m_bAimingAtYou") == trueend)
events.esp_player:set(function(p, e)
if p:is_enemy() and p:get_prop("m_bDowned") then e:set_color(color(255, 140, 50)) endend)
events.render:set(function()
for _, m inipairs(entity.get_entities("machine", 80)) doif m.target == "you"thenlocal s = m.origin and m.origin:to_screen()
if s then render.text(nil, s, color(255, 60, 60), "cs", m.name .. " is after you") endendendend)
GTA V Enhanced
elusive.product == "grand-theft-auto-v-enhanced"game.name() == "Grand Theft Auto V Enhanced"Player ESP hook
game.get_map()
nil (no map in GTA)
entity.get_players()
every ped of the ESP's snapshot, drawn or not: players and NPCs
World axes
Z up
is_enemy()
not you (there are no teams)
entity.get_entities()
"vehicle": occupants, has_player, has_local, velocity; an occupied vehicle has its first occupant's position (a meter or two off), an empty one none
ESP elements esp.elements()
health
armor
weapon
ammo
godmode
invisible
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
spine2
spine1
spine0
pelvis
clavicle_l
clavicle_r
upper_arm_l
upper_arm_r
lower_arm_l
lower_arm_r
hand_l
hand_r
upper_leg_l
upper_leg_r
lower_leg_l
lower_leg_r
foot_l
foot_r
Fields p:get_prop(name), any other name gives nil
Pool
m_fHealth
m_fMaxHealth
m_nArmour
Weapon
m_StatName
m_WeaponRange
m_iAmmoInClip
m_iAmmoTotal
State & flags
m_State
m_deathStateBits
m_baseFlags
m_aFlags
m_PedConfigFlags1
m_PedResetFlags1
m_nPhysicalFlags
m_bIsVisible
m_isControlledBy
Synthesised
IsPlayer
Name & position
m_Name
m_Position
m_Velocity
m_vDesiredVelocity
m_Speed
m_BonesValid
m_uGlobalTime
m_uTimer
entity.get_players() lists every ped of the ESP's snapshot, drawn or not: players and NPCs. Split them with get_prop("IsPlayer").
Health is as the ESP shows it (m_fHealth minus GTA's 100 death threshold); armour 0..100.
get_weapon() is "UNARMED" when the ped carries nothing; get_name() is nil for a non-player ped.
No teams: is_enemy() is "not you". No map name.
get_hitbox_position(name) is nil for a bone not read this tick.
Example for GTA V Enhanced
local box = ui.create("GTA Demo", "Users"):group("ESP")
local showNpc = box:checkbox("Keep NPCs on screen", true)
events.esp_player:set(function(p, e)
ifnot p:get_prop("IsPlayer") thenifnot showNpc:get() thenreturn e:hide() end
e:set_color(color(140, 140, 140)) -- grey out NPCselse
e:set_color(color(255, 100, 100)) -- red for playersendend)
GTA V Legacy
elusive.product == "grand-theft-auto-v-legacy"game.name() == "Grand Theft Auto V Legacy"Player ESP hook
game.get_map()
nil (no map in GTA)
entity.get_players()
every ped of the ESP's snapshot, drawn or not: players and NPCs
World axes
Z up
is_enemy()
not you (there are no teams)
game.view_matrix()
the clip-z row is zeros (not read); x / y projection is unaffected
entity.get_entities()
"vehicle": occupants, has_player, has_local, velocity; an occupied vehicle has its first occupant's position (a meter or two off), an empty one none
ESP elements esp.elements()
health
armor
weapon
ammo
godmode
invisible
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
spine2
spine1
spine0
pelvis
clavicle_l
clavicle_r
upper_arm_l
upper_arm_r
lower_arm_l
lower_arm_r
hand_l
hand_r
upper_leg_l
upper_leg_r
lower_leg_l
lower_leg_r
foot_l
foot_r
Fields p:get_prop(name), any other name gives nil
Pool
m_fHealth
m_fMaxHealth
m_nArmour
Weapon
m_StatName
m_WeaponRange
m_iAmmoInClip
m_iAmmoTotal
State & flags
m_State
m_deathStateBits
m_baseFlags
m_aFlags
m_PedConfigFlags1
m_PedResetFlags1
m_nPhysicalFlags
m_bIsVisible
m_isControlledBy
Synthesised
IsPlayer
Name & position
m_Name
m_Position
m_Velocity
m_vDesiredVelocity
m_Speed
m_BonesValid
m_uGlobalTime
m_uTimer
Same data as the Enhanced version.
entity.get_players() lists every ped of the ESP's snapshot, drawn or not: players and NPCs. Split them with get_prop("IsPlayer").
Health is as the ESP shows it (m_fHealth minus GTA's 100 death threshold); armour 0..100.
get_weapon() is "UNARMED" when the ped carries nothing; get_name() is nil for a non-player ped.
No teams: is_enemy() is "not you". No map name.
get_hitbox_position(name) is nil for a bone not read this tick.
Example for GTA V Legacy
local box = ui.create("GTA Demo", "Users"):group("ESP")
local showNpc = box:checkbox("Keep NPCs on screen", true)
events.esp_player:set(function(p, e)
ifnot p:get_prop("IsPlayer") thenifnot showNpc:get() thenreturn e:hide() end
e:set_color(color(140, 140, 140)) -- grey out NPCselse
e:set_color(color(255, 100, 100)) -- red for playersendend)
Red Dead Redemption 2
elusive.product == "red-dead-redemption-2"game.name() == "Red Dead Redemption 2"Player ESP hook
game.get_map()
nil (no map label)
entity.get_players()
every ped of the ESP's snapshot: players, NPCs, animals (get_prop("IsPlayer") / "IsAnimal")
World axes
Z up
is_enemy()
not you (there are no teams)
get_name()
only on RedM; nil on single-player
get_weapon()
nil when the ped is unarmed
entity.get_entities()
none ({}): every ped, animal and horse is in get_players()
ESP elements esp.elements()
health
weapon
ammo
godmode
invisible
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
upper_stomach
stomach
lower_stomach
pelvis
clavicle_l
clavicle_r
upper_arm_l
upper_arm_r
forearm_l
forearm_r
hand_l
hand_r
thigh_l
thigh_r
shin_l
shin_r
foot_l
foot_r
Fields p:get_prop(name), any other name gives nil
Pool
m_fHealth
m_fMaxHealth
m_HealthFlags
m_DeathState
Weapon
m_WeaponName
m_WeaponHash
m_WeaponRange
m_iAmmoInClip
State
m_State
m_baseFlags
m_AttachmentFlags
m_bIsVisible
m_bIsLocal
m_nType
Synthesised
IsPlayer
IsAnimal
Name & position
m_Name
m_Position
m_Velocity
No armor.
Example for Red Dead Redemption 2
local g = ui.create("RDR2 Demo", "Target"):group("Filters")
local showAnimals = g:checkbox("Animals", false)
events.esp_player:set(function(p, e)
if p:get_prop("IsAnimal") thenifnot showAnimals:get() thenreturn e:hide() end
e:set_color(color(120, 220, 120))
elseifnot p:get_prop("IsPlayer") then
e:set_color(color(160, 160, 160)) -- NPCsendend)
esp.flag("LOW HP", color(255, 70, 70), function(p)
local hp = p:get_health()
return p:is_enemy() and hp ~= niland hp > 0and hp < 25end)
every player state of the match with a pawn: players and bots (m_bIsABot)
World axes
Z up, meters
is_enemy()
not local and not on the local player's team (same TeamIndex)
get_team()
the player state's TeamIndex (nil when unknown or 0)
get_health()
the local player only: the game does not send the others' health (nil). get_armor() is nil (the local player's shield is m_iShield)
get_weapon()
the held item's English name ("Assault Rifle"), nil when unknown
entity.get_entities()
"loot" (the loot of every level of the island the game has loaded: category "container" / "item" / "supply_drop" / "llama"; containers: already_searched, start_already_searched, loot_tier; items: rarity 0 Common, 1 Uncommon, 2 Rare, 3 Epic, 4 Legendary, 5 Mythic, 6 Exotic, stack_size, level, item_kind, item_category as in entity.get_catalog(), and when the item catalog knows the definition: definition, name_local, tier, tags), "vehicle" (cars, boats and the other vehicles: the name only)
entity.get_catalog()
every item the loot pages list by its English name, once per name (the rarities share it), category "Weapons", "Ammo", "Materials", "Consumables", "Traps" or "Gadgets"; {} until the item catalog is read
ESP elements esp.elements()
health
shield
status
action
bot
platform
team
weapon
name
distance
Hitboxes p:get_hitbox_position(name)
head
neck
chest
pelvis
upper_arm_l
upper_arm_r
lower_arm_l
lower_arm_r
hand_l
hand_r
upper_leg_l
upper_leg_r
lower_leg_l
lower_leg_r
ankle_l
ankle_r
Fields p:get_prop(name), any other name gives nil
Health (local player only)
m_hasHealth
m_iHealth
m_iMaxHealth
m_iShield
m_iMaxShield
State
m_bAlive
m_bDowned
m_bIsDying
m_bTeammate
m_bIsLocalPlayerController
m_isVisible
m_hasBones
Player state
m_iTeamIndex
m_iSquadId
m_iKillScore
m_szPlatform
m_bIsABot
m_bIsDisconnected
m_iPing
Pawn state (every 50 ms)
m_bADS
m_bIsSkydiving
m_bIsParachuteOpen
m_bReloading
Held weapon
m_szWeaponName
m_szWeaponTag
m_iWeaponRarity
m_iAmmo
Name & position
m_iszPlayerName
m_vecOrigin
m_vecAbsVelocity
m_vecViewOffset
health / shield never show on an enemy: the game sends health for the local player only, whom the ESP does not draw. status is DBNO on a knocked down player (bIsDBNO) and DISCONNECTED on the pawn a player left in the world; action is RELOAD, ADS, SKYDIVE or PARACHUTE; team is "T3 (2)" (his TeamIndex and how many of his team are still up); weapon is "Assault Rifle [25]" (name and rounds in the magazine).
m_bAlive counts a knocked down player (m_bDowned); m_bIsDying is the pawn dying. m_isVisible is a clear line from the camera to the head, chest or pelvis through the map's static collision (true for everyone until the collision is read).
m_iTeamIndex: teammates share it (0 = no team); m_iPing in ms, the local player only. m_szWeaponTag is the held weapon's first Weapon.* gameplay tag ("Weapon.Ranged.SMG"). Hitboxes only when the skeleton was read (m_hasBones).
Example for Fortnite
esp.flag("5+ KILLS", color(255, 200, 80), function(p)
local k = p:get_prop("m_iKillScore")
return p:is_enemy() and k ~= niland k >= 5end)
events.esp_player:set(function(p, e)
if p:is_enemy() and p:get_prop("m_bDowned") then
e:set_color(color(255, 140, 50))
elseif p:get_prop("m_bIsABot") then
e:set_color(color(160, 160, 160))
endend)
-- legendary and better items within 50 m
events.render:set(function()
for _, it inipairs(entity.get_entities("loot", 50)) doif it.category == "item"and (it.rarity or0) >= 4and it.origin thenlocal s = it.origin:to_screen()
if s then render.text(nil, s, color(255, 150, 40), "co", it.name or"item") endendendend)
Everything this state exposes, found by walking it rather than from a list kept by hand: { [name] = { kind, fields, functions, operators } }. kind is "module" (render, ui, string...), "type" (vector, color: their constructor statics and methods), "object" (player, ui_element, font...: methods and operators) or "global" (the entry "functions": print, pairs, require...). With a name, that one entry, or nil. Plain tables, safe to walk. A thread sees its own, smaller API.
Returnstable | nil
Example
local api = elusive.list()
for name, e inpairs(api) doif e.kind == "module"thenprint(name, #e.functions .. " functions") endendprint(table.concat(elusive.list("player").functions, ", "))
The same, printed to the console: one line per module without a name (fields, functions, operators), one line per function with a name (player:get_health()...). The quickest way to see what exists in the product you are on. An unknown name raises, with a hint.
Example
elusive.dump() -- every module on one line each
elusive.dump("render") -- one function per line
elusive.dump("player") -- the methods and operators of an object
Loads the script again from its file (or the code it was loaded with) once the callback returns. A syntax error keeps the running version and is logged.
Lines to the in-game console (Configs & scripts > Lua) and the /dashboard/live console, under the script's name. __tostring aware; a line is cut at 16 KB.
Example
print("hello from", script.name)
log.warn("something to look at")
Loads a library once per script: <scripts>\libs\name.lua, then <scripts>\name.lua ("a.b" becomes a\b.lua; a name ending in .lua is used as is), then a script followed on the site by its title (any case, libraries first). The chunk gets the name as "..."; a library that returns nothing gives true.
Parameter
Type
Description
name
string
Letters, digits, "_ - . /" and spaces, 128 characters at most, no "..".
A require loop raises. Each script has its own require cache: a library runs once per script, in that script's state.
Example
-- <scripts>\libs\utils.lua returns { pct = function(a, b) return math.round(a / b * 100) .. "%" end }local utils = require("utils")
print(utils.pct(1, 3)) -- 33%
A named channel between scripts. :call(...) runs every subscriber of that name, in every script (the caller's own too), with copies of the arguments: numbers, strings, booleans, plain tables, vector and color. Functions, API objects and cyclic tables are refused. An error in a subscriber never reaches the caller.
Parameter
Type
Description
name
string
1 to 128 characters.
...
values
Up to 16 arguments; 32 levels, 200,000 values, 64 MB at most.
Custom events nest 16 deep at most. The time another script spends in its handler is not charged to the caller, up to twice the caller's budget in all.
Example
-- script A
events.custom("kills"):set(function(n) print("A got", n) end)
-- script B
events.custom("kills"):call(12)
Starts fn(...) on a new thread (up to 16 arguments). A string is run as code. Inside: thread.recv, thread.send, thread.sleep, thread.stopping, thread.id, print, log, require, json, vector / color, input, common, clipboard, os, files, the libraries, and the blocking forms of network / memory / shellcode / serial / kmbox / os.execute the script's permissions allow. No render, ui, hud, events, timer, game / entity / esp or configs.
Returnsa thread object.
Example
local t = thread.spawn(function(prefix, n)
whilenot thread.stopping() dolocal msg = thread.recv(1)
if msg then thread.send(prefix .. msg .. " x" .. n) endendend, "echo: ", 3)
t:set_callback(function(v) print(v) end)
t:send("hello")
Sends a value to the thread / takes one the thread sent, without waiting. send returns false when the queue is full (1024 values / 32 MB each way) or the thread has ended.
A toggle. A checkbox can take companions on the same row: enabled:color(default) and enabled:hotkey(vk, mode).
Example
local g = ui.create("Demo"):group("Box")
local enabled = g:checkbox("Enabled", false)
local col = enabled:color(color(255, 255, 255))
local key = enabled:hotkey(0x05, "toggle") -- mouse 4, "hold" | "toggle" | "always"
group:slider(label, min, max, default [, opts]) -> element
A slider. Integer when min, max and default are integers (unless opts.float = true), else a float. opts: { unit = "°", step = 0.05, float = true }. The default is min when omitted.
Example
local g = ui.create("Demo"):group("Sliders")
local fov = g:slider("FOV", 1, 180, 30, { unit = "°" })
local smooth = g:slider("Smooth", 0.0, 1.0, 0.35, { step = 0.05 })
A keybind row: left click binds a key, right click picks the mode ("hold" | "toggle" | "always"). Listed in the keybinds HUD. :get() tells whether it is active; :key(), :mode(); :set(vk [, mode]).
Example
local key = ui.create("Demo"):group("Keys"):hotkey("Aim key", 0x02, "hold")
events.render:set(function()
if key:get() then render.text(nil, vector(20, 60), color(255, 200, 0), "s", "aiming") endend)
The value: boolean for a checkbox, number for a slider, the option's name for a combo, a table of names for a multi, a color, active? for a hotkey, its text for a label / input; nil for a button or a separator.
Changes the value: combo a name or a 1-based index, slider clamped to its range, multi a table of names or set(name, on), hotkey set(vk [, mode]), label its text. Chainable. A wrong type, a button or a separator raises.
fn(el) when the value changes (in game, from /dashboard/live or when a config loads); a button calls it on click. now = true also calls it right away. 16 per element; the same function twice is ignored; removed after running out of the time budget or raising 64 times in a row.
Example
local fov = ui.create("Demo"):group("Aim"):slider("FOV", 1, 180, 30)
fov:set_callback(function(el) print("FOV is now", el:get()) end, true)
el:depend(other, value) el:depend({ a, v1 }, { b, v2 })
Shown only while every condition holds: no value = truthy; checkbox true / false; combo a name or an index; multi that option selected; slider that number; input that text. 32 conditions at most.
Example
local g = ui.create("Demo"):group("Aim")
local on = g:checkbox("Enabled", false)
local fov = g:slider("FOV", 1, 180, 30)
fov:depend(on, true) -- hidden while the box is unticked
One of the cheat's own settings ("aimbot.enabled"…) as the same kind of object: get, set, set_callback. Another script's element or an unknown key raises; the runtime's keys (lua.*) and menu.key are read-only.
Example
local box = ui.find("visuals.players.box")
box:set_callback(function(el) print("box ESP:", el:get()) end, true)
A world position projected with the game's camera: a screen vector, or nil off screen (always nil in a thread).
Example
events.render:set(function()
for _, p inipairs(entity.get_players(true)) dolocal head = p:get_hitbox_position("head")
local s = head and head:to_screen()
if s then render.circle(s, 6, color(255, 255, 255), 1.5, 24) endendend)
Text in a font (nil: the Appearance font, the ESP's). flags: "c" centered, "r" right-aligned, "m" vertically centered, "o" outline, "s" shadow (combine them: "cs"). 64 KB of text at most.
Example
events.render:set(function()
local s = render.screen_size()
render.text(nil, vector(s.x / 2, 20), color(255, 255, 255), "cs", "centered with a shadow")
end)
A system font ("Verdana") or a .ttf / .otf / .ttc path (relative: the cheat's fonts folder; 16 MB at most). flags: "b" bold, "i" italic, "m" no antialiasing. Size 1 to 256 px. Usable from the next frame (font:ready()); the default font is drawn until then. font:size(), font:name(). 8 per script.
Example
local font = render.load_font("Verdana", 18, "b")
events.render:set(function()
render.text(font, vector(20, 200), color(255, 255, 255), "s", "bold Verdana 18")
end)
PNG / JPG / BMP / GIF, from bytes or a path (inside the script's data folder, or anywhere with the files permission). image:size() -> vector, image:unload(). 8192 x 8192 px and 64 MB encoded at most; 64 per script, counted in its memory.
Makes the window (or gives the same one again, with the new options). opts: title, icon, width (2000 max), header (false: no title bar), anchor 0-8 (0 top-left, 4 centre, 8 bottom-right), x, y.
Example
local w = hud.window("stats", { title = "Stats", icon = "target", width = 220, anchor = 0, x = 20, y = 200 })
events.render:set(function()
w:clear()
w:row("Kills", "12", { right_color = color(120, 220, 120) })
w:bar("Health", 0.66, { text = "66" })
w:text("centered", { center = true, color = color(255, 200, 0) })
end)
w:clear() w:row(left, right [, opts]) w:text(text [, opts]) w:bar(label, value [, opts]) w:separator()
Items, filled again every frame after w:clear(). row opts: active, color, right_color; text opts: active, center, color; bar opts (value 0..1): text, active, color, label_color. Texts go through tostring and are cut at 256 bytes.
Retitle; change the icon (no name removes it; eye, eye_off, keyboard, users, user, list, target, clock, alert, info, star, heart, map, lock, bell, trophy, flag, zap, home, settings, code); show / hide (no argument shows); remove it (hud.window(name) again brings it back). The methods return the window, except remove.
You; every player of the snapshot (true: enemies only); the player with that index (as player:get_index() and the ESP hook give it). At most 1024 players per frame.
Example
events.render:set(function()
ifnot game.is_in_game() thenreturnendfor _, p inipairs(entity.get_players(true)) dolocal pos, size = p:get_bbox()
if pos and p:is_alive() then
render.text(nil, vector(pos.x + size.x / 2, pos.y - 14), color(255, 255, 255), "co", p:get_name() or"?")
endendend)
The world's other things (vehicles, loot, NPCs, objectives, ARC machines…) as plain tables, copied from the lists the product already keeps, only when a script asks (once per frame). Each has kind (one of get_entity_kinds()), id (an integer, stable while it exists), name, origin (vector), distance (from the local player) and the kind's own fields (see "Per game"); a value that is not known is nil. kind keeps one category (1 to 64 bytes); max_distance keeps the ones within it. At most 16384 per frame. The tables are copies: changing one changes nothing.
A game without such lists gives {} for both (Red Dead Redemption 2: every ped is already in get_players()).
Example
events.render:set(function()
ifnot game.is_in_game() thenreturnendfor _, e inipairs(entity.get_entities(nil, 60)) dolocal s = e.origin and e.origin:to_screen()
if s then
render.text(nil, s, color(255, 220, 120), "cs", string.format("%s %s %.0fm", e.kind, e.name or"?", e.distance or0))
endendend)
Everything the game has of the kind the product lists, on the map or not (Rust: every item of the game, one per name, skins under their base item; category as in the menu: "Weapons", "Ammo", "Medical"…; Arc Raiders: every item of the game, category "Weapons", "Materials", "Shields"…). A fresh copy at each call: call it once, not every frame.
{} when the game has none or has not loaded it yet.
Example
local items = entity.get_catalog()
print(#items .. " items")
for i = 1, math.min(5, #items) doprint(items[i].name, items[i].category)
end
Identity and pool. A player object stays valid while that player is in the snapshot; after that its getters return nil. Two objects for the same player are equal (==).
The box the product's ESP drew him in last frame (vectors); nil when it did not: off screen, ESP off. Inside events.esp_player, e:box() is this frame's.
A field of the product's snapshot by its name (CS2 "m_iHealth", "m_bIsScoped"; Overwatch "m_flUltCharge"…): number, boolean, string or vector. Any other name gives nil; a name of 1 to 128 bytes.
Example
events.render:set(function()
for _, p inipairs(entity.get_players(true)) dolocal s = p:get_origin() and p:get_origin():to_screen()
if s and p:get_prop("m_bIsScoped") then render.text(nil, s, color(0, 200, 255), "cs", "SCOPED") endendend)
For each player the ESP draws, every frame, inside the frame: p is the player (as entity.get_player gives it), e this player's ESP for this frame. render.* works in the callback (drawn under the ESP's labels). Several scripts and callbacks can edit the same player, in order, seeing each other's changes.
e only works inside the callback it was given to: kept for later, any method raises. Texts are cut at 128 bytes; 32 texts / bars added per player per script.
Example
events.esp_player:set(function(p, e)
local hp = p:get_health() or0ifnot p:is_enemy() or hp < 20or hp > 50thenreturn e:hide() end
e:set_color(color(255, 200, 0))
e:set_weapon(string.upper(p:get_weapon() or""))
e:add_text("right", "TARGET", color(255, 60, 60))
e:add_bar("right", hp / 100, color(255, 60, 60), 3)
end)
Replaces the text of one of the ESP's elements; id is one of esp.elements() ("name", "weapon", "health", "distance"…). An id the product does not have does nothing. set_name / set_weapon are shortcuts.
The label name on every player for which fn(player) returns true. A real ESP element: the script gets an "ESP flags" tab with a toggle, a color and a position per flag, saved in the configs and movable in the ESP preview. opts: { side = "right", enabled = true }. 32 flags per script; names up to 64 bytes, unique in a script.
A flag whose function raises 64 times in a row is removed; when a script's flags run out of the time budget, all of them are removed.
The ids of the product's ESP elements ({ "health", "armor", "weapon", "ammo", "name", "distance" } in CS2…), for set_text / set_element_color / hide_element.
The Windows virtual-key codes by name without the VK_ prefix: LBUTTON, RBUTTON, RETURN, ESCAPE, SPACE, SHIFT, CONTROL, MENU, PRIOR, NEXT, INSERT, DELETE, F1..F12, NUMPAD0..9, OEM_1… Letters and digits are their ASCII codes. Read-only.
Need the configs permission. true, or nil plus "invalid config name" / "config not found" / "not a config file" / "cannot write the file". Names: 1 to 64 bytes, no path characters, .json optional. Loading fires events.config_loaded(name) from the next frame and the menu elements' callbacks.
A mark on the viewers' map at world x / y (the players' coordinates: an origin works). Marking the same id again moves / restyles it. opts: label (32 characters), color (a color, orange by default), shape ("pin" default, "dot", "square", "diamond", "triangle", "ring"), size (0.5 to 3), pulse (a ring keeps growing around it). Sent with every radar frame while someone watches; the viewers can hide them (World > Script marks).
64 marks per script, 256 in all (a new one past that raises); a script's marks go when it unloads. A bad id, position, shape or color raises.
Example
local me = entity.get_local_player()
local at = me and me:get_origin()
if at then
radar.mark("here", at, { label = "Here", color = color(255, 90, 90), pulse = true })
radar.mark("north", at.x, at.y + 50, { label = "50 m north", shape = "triangle" })
end
The stock functions with a step budget per call: a catastrophic backtracking pattern raises "too slow" instead of freezing; a string.rep that is too large raises.
The stock functions, bounded: raw length and raw reads (a lying __len has no effect), table.move 4,194,304 elements at most, table.sort 500,000 at most and safe with any comparator.
parse: null becomes nil; nesting over 500 levels raises. stringify: a 1..n table becomes an array, else an object ({} for an empty table); raises on a function / userdata, a key that is not a string or a number, keys 1 and "1" in one table, a cycle, over 200 levels, 1,000,000 values or 64 MB.
Example
local t = json.parse('{"a": 1, "b": [1, 2, 3]}')
print(t.b[2], json.stringify({ x = 1, list = { 1, 2 } }))
ECMAScript syntax: classes, \d \w \s \b, groups, (?:), |, greedy and lazy * + ? {n,m}, \xHH, \uHHHH as UTF-8; no backreferences, lookarounds or named groups (an error says so). A linear engine: any pattern answers in time proportional to the subject and never freezes. groups gives {} without a match and "" for an unused group; replace takes $1…$99, $&, $`, $', $$.
64 KiB subject and replacement, 512-byte pattern, 32 groups, {n,m} up to 1000, find_all 10,000 matches, replace result 4 MB; a step budget raises past about 0.1 s.
cb(res, err): res = { status, body, headers } or nil plus a reason. options: headers (a map or { "Name: value", ... }, 64 at most), body, timeout (1 to 60 s, 30 by default); request also takes url and method (GET, POST, PUT, PATCH, DELETE, HEAD). url: http:// or https://, 8192 bytes at most.
Example
--@permissions networklocal status = "loading…"
network.get("https://api.github.com/zen", { timeout = 10 }, function(res, err)
status = res and res.body or ("error: " .. err)
end)
events.render:set(function()
render.text(nil, vector(20, 60), color(255, 255, 255), "s", status)
end)
Runs cmd /c command without a window (under 8000 bytes); cb(code, output) with stdout + stderr (1 MB kept), or cb(nil, err). After 60 s the command and every process it started are stopped. In a thread, without a callback: code, output | nil, err. os permission.
Example
--@permissions os
os.execute("echo hello", function(code, output) print(code, output) end)
--@permissions memorylocal base = memory.module_base("client.dll")
if base thenprint(string.format("client.dll at 0x%X, first bytes 0x%X", base, memory.read_u32(base) or0)) end
Many reads in one scatter round trip (4096 entries; 4 MB in a script, 64 MB in a thread). false for an invalid or oversized entry; unreadable bytes come back as zeros; nil when not attached.
A scan in a module / a range (512 MB at most): cb(address | nil). "48 8B 05 ? ? ? ?" with ? wildcards; instr_len / disp resolve a RIP-relative operand. The first scan of a module reads it once and keeps a copy (memory.release_images() frees them).
Writes code (5 B to 64 KB, ending with E9 00 00 00 00: the jump back to from + stolen) into the cave (5 B to 1 MB, at least #code) and a jmp to it at from over stolen bytes (5 to 256).
open: cb(true | nil, err); baud 300 to 12,000,000, 115200 by default. connect: tries the port (or the first of find_ports()), sends the probe ("km.version()" by default, "" for none): cb({ port, baud, reply [, warning] } | nil, err).
The header line: a comma-separated list of files, network, clipboard, memory, device, os, configs. Without any permission a script already has the menu, drawing, the HUD, the game, input, the standard library, json / crypto / regex, files.* in its own data folder, configs.list / current, network.url_encode / url_decode, threads and events.
Example
--@permissions network
network.get("https://api.github.com/zen", function(res, err)
print(res and res.body or err)
end)
1 s per callback, 5 s for the top level. A callback that runs over is removed (even inside a pcall, or in a coroutine); 3 overruns in a minute unload the script. 64 errors in a row remove a callback or a timer.
memory.* (same names); pattern, signature, modules, processes, sections, imports, threads, vad, handles, dump_module take a callback in a script; read_vec3 gives a vector and write_vec3 takes one; read_batch gives false for an invalid entry.