Globals ​
Top-level functions and small namespaces (storage) callable from any script. These don't need a prefix beyond the namespace name.
Player Access ​
| Function | Returns | Description |
|---|---|---|
local_player() | player | The local player object |
get_players() | table | All enemy players. Teammates are always available via get_teammates() |
get_all_players() | table | All valid players except the local player |
get_teammates() | table | Teammate players only |
local me = local_player()
for _, p in ipairs(get_players()) do
if p:is_enemy() and p:is_alive() then
-- ...
end
endWorld Entities ​
| Function | Returns | Description |
|---|---|---|
get_world_entities([type]) | WorldEntity[] | All tracked world entities. Optional type string filters by coarse type name (e.g. "sinner", "tower") or exact engine class name (e.g. "citadel_breakableprop") |
Each WorldEntity has: type, class_name, x, y, z, health, max_health, team.
-- Print all sinners (neutral soul-denying enemies)
for _, e in ipairs(get_world_entities("sinner")) do
print(string.format("sinner at %.0f,%.0f hp=%d/%d", e.x, e.y, e.health, e.max_health))
end
-- Check for breakable props regardless of sub-type
for _, e in ipairs(get_world_entities("citadel_breakableprop")) do
print(e.class_name, e.subclass)
endTime ​
| Function | Returns | Description |
|---|---|---|
clock() | number | High-resolution time in seconds. Use for deltatime and debouncing |
game_time() | number | Match clock time in seconds (pause-aware HUD timer) |
get_match_time() | number | Alias for game_time() |
sim_time() | number | Raw server SimTime; the clock for ability phase timestamps |
clock() is wall-clock monotonic seconds since the engine started. game_time() (and its alias get_match_time()) advances with the match clock and freezes when the game is paused.
sim_time() is distinct from both: it is the absolute server timestamp the game uses internally for ability phase fields like cast_delay_start, cooldown_end, and modifier expires_at. Always compare those fields against sim_time(), not game_time() or clock().
-- Check how long a cast delay has been running
local ab = local_player():get_ability(slot.ability1)
if ab and ab.is_in_cast_delay then
local elapsed = sim_time() - ab.cast_delay_start
print("Cast delay running for " .. elapsed .. "s")
endOutput ​
| Function | Returns | Description |
|---|---|---|
print(...) | - | Output to app console and Windows console. Auto-prefixed with [Lua:script_name] |
toast(text, [duration], [style]) | - | Centered HUD toast. Optional per-call styling. See below |
notify(text, [duration], [color]) | - | Persistent notification. Default duration 3.0, color white |
play_sound(filename) | - | Play a WAV file from assets\sounds\. Pass the filename only, no path separators |
toast("ready")
notify("warning", 5.0)
play_sound("alert.wav") -- plays assets\sounds\alert.wavtoast(text, [duration], [style]) ​
A centered HUD toast notification.
duration, seconds to display. Pass0to use the global default from menu settings.style, optional table with per-call overrides. Any key omitted falls back to the global defaults configured in the menu:
| Key | Type | Notes |
|---|---|---|
bg | {r, g, b, a} | Background fill |
text | {r, g, b, a} | Text color |
outline | {r, g, b, a} | Outline / drop-shadow color |
scale | number | Size multiplier (1.0 = default) |
Toasts deduplicate by text only. If the same string is currently showing or already queued, the call is dropped on the floor, even if you passed a different style table. This means it's safe to fire toast("X") inside on_tick without flooding the screen; a tick-rate spam of the same message renders the original once.
toast("Hit", 2, {bg = draw.color(255,0,0)}) -- shows
toast("Hit", 2, {bg = draw.color(0,255,0)}) -- DEDUPED (same text)
toast("Hit!", 2) -- shows (different text)Different toasts with different text aren't deduplicated against each other, only repeats of the same string are suppressed, so a stream of "Hit X", "Hit Y", "Hit Z" all appear. The queue holds at most 5 pending toasts at a time.
toast("Target locked", 2, {
bg = {r = 40, g = 15, b = 50, a = 220},
text = {r = 200, g = 140, b = 255},
scale = 1.2
})Demo script - each button triggers a different style preset:
-- Toast Style Demo
-- Click buttons to see different toast styles
name = "Toast Style Demo"
description = "Showcases custom toast styling"
ui.new_tab("toast_demo", "Toast Demo")
ui.button("toast_demo", "styles", "Default Toast", function()
toast("Default style, uses your global settings", 3)
end)
ui.button("toast_demo", "styles", "Success (Green)", function()
toast("Parry successful!", 3, {
bg = {r=20, g=60, b=20, a=220},
text = {r=100, g=255, b=100},
outline = {r=80, g=200, b=80, a=120},
})
end)
ui.button("toast_demo", "styles", "Danger (Red)", function()
toast("LOW HP, retreat!", 4, {
bg = {r=80, g=10, b=10, a=230},
text = {r=255, g=80, b=80},
outline = {r=255, g=60, b=60, a=150},
scale = 1.3,
})
end)
ui.button("toast_demo", "styles", "Info (Blue)", function()
toast("Cooldown ready", 2, {
bg = {r=15, g=25, b=60, a=220},
text = {r=120, g=180, b=255},
outline = {r=80, g=140, b=220, a=100},
})
end)
ui.button("toast_demo", "styles", "Big Gold", function()
toast("ULTRA KILL!", 5, {
bg = {r=40, g=30, b=5, a=240},
text = {r=255, g=215, b=0},
outline = {r=255, g=180, b=0, a=160},
scale = 1.8,
})
end)Input shortcuts ​
| Function | Returns | Description |
|---|---|---|
slot_to_key(slot) | integer | VK code bound to the given ability/item slot. See Game Keybinds |
item_slot_to_key(config_slot) | integer | VK code for a 0-based item config slot (0-3). Equivalent to slot_to_key(config_slot + 4) |
left_click() | - | Simulate a left mouse click |
right_click() | - | Simulate a right mouse click |
For finer-grained input control (key down, key up, mouse movement), see the Input namespace.
Game state ​
is_position_visible(pos) ​
Performs a BVH raycast from the camera to a world-space position. Returns true if the path is unobstructed (the point is visible from the camera).
local pos = some_player:get_position()
if is_position_visible(pos) then
-- position has line-of-sight from camera
endThis is a point-in-world check, not a player-visibility check. For per-player visibility use player:is_visible() instead.
is_in_menu() ​
Returns true when the game cursor is visible, meaning the player is in a menu, shop, scoreboard, or settings screen.
if is_in_menu() then return endAll input actions (key presses, mouse movement) from Lua scripts are automatically blocked while in menu. You don't need to check is_in_menu() for safety, it's handled internally. Use this to skip expensive logic like targeting when the player isn't actively playing.
is_game_focused() ​
Returns true when the game window is in the foreground (active and focused).
if not is_game_focused() then return endUse this to skip logic when the player has alt-tabbed or is in another window. Unlike is_in_menu(), this detects when the game itself isn't the active window.
Spectators ​
The game namespace exposes who is currently spectating the match.
| Function | Returns | Description |
|---|---|---|
game.get_spectators() | table | Array of spectator name strings currently watching the match |
game.get_spectator_count() | integer | Number of spectators, without building the name table |
if game.get_spectator_count() > 0 then
local specs = game.get_spectators()
print(#specs .. " spectators: " .. table.concat(specs, ", "))
endTsuki Config ​
Read/write the cheat's own settings (ESP toggles, aimbot FOV, etc.).
| Function | Returns | Description |
|---|---|---|
tsuki.get_bool(key, default) | boolean | Read a boolean setting |
tsuki.get_int(key, default) | integer | Read an integer setting |
tsuki.get_float(key, default) | number | Read a float setting |
tsuki.set_bool(key, value) | - | Write a boolean setting |
tsuki.set_int(key, value) | - | Write an integer setting |
tsuki.set_float(key, value) | - | Write a float setting |
tsuki.get_color(key, default) | Color | Read a color {r,g,b,a} (0-255) |
tsuki.set_color(key, value) | - | Write a color {r,g,b,a} (0-255) |
-- React to the user's configured aimbot FOV
local fov = tsuki.get_float("aimbot_fov", 3.0)
local target = targeting.find_closest_by_fov(fov, 30.0)
-- Temporarily override a setting and restore it
local prev = tsuki.get_bool("show_teammate_esp", false)
tsuki.set_bool("show_teammate_esp", false)
-- ... do something ...
tsuki.set_bool("show_teammate_esp", prev)Colors are stored as [r,g,b,a] arrays (each 0-255); get_color/set_color use a matching {r,g,b,a} table:
-- Tint the enemy box fill red while a condition holds, restore otherwise
local KEY = "enemy_box_fill_vis_color"
local base = tsuki.get_color(KEY, {r=255, g=255, b=255, a=40}) -- cache the real color ONCE
if condition then
tsuki.set_color(KEY, {r=255, g=0, b=0, a=90})
else
tsuki.set_color(KEY, base) -- restore
endset_color writes the LIVE setting
set_color changes the user's actual saved value and it persists to their config. For a temporary or per-modifier tint, cache the original with get_color and restore it when the condition clears - otherwise the tint can be saved permanently if the game closes while it is active.
Setting keys match the IDs the cheat uses internally. For new script-defined settings, prefer the UI namespace - it creates a labeled widget and persists its value automatically.
Cross-Script Modules ​
Scripts can share code by using export and import. A library script calls export at the top level to register a table of functions under a name. Consumer scripts call import from on_tick to fetch it.
| Function | Returns | Description |
|---|---|---|
export(name, module_table) | - | Register a table under name so other scripts can import it. Call at top level |
import(name) | table | nil | Fetch a module exported by another loaded script. Returns nil if the library is not loaded |
WARNING
require, dofile, and loadfile are blocked. export/import is the only supported mechanism for sharing code between scripts.
-- mylib.lua - the library script
name = "mylib"
description = "Shared utilities"
export("mylib", {
clamp = function(v, lo, hi) return math.max(lo, math.min(hi, v)) end,
sign = function(v) return v >= 0 and 1 or -1 end,
})-- consumer.lua
name = "consumer"
description = "Uses mylib"
local lib = nil
function on_tick()
if not lib then lib = import("mylib") end
if not lib then return end -- library not loaded yet
local clamped = lib.clamp(some_value, 0, 100)
endStorage ​
Persistent key-value storage scoped per script. Values survive script reloads, game restarts, and PC restarts. Each script gets its own isolated storage at scripts\storage\<script_name>.json; keys from script A are invisible to script B.
| Function | Returns | Description |
|---|---|---|
storage.set(key, value) | - | Save a value under a key. Passing nil as the value deletes the key. |
storage.get(key) | any | nil | Load a saved value, or nil if not set |
storage.keys() | table | List of saved keys for this script |
Values can be strings, numbers (integer or float), booleans, or nil. Tables are not supported - store fields individually or encode manually as a string if you need structured data. Writes are cached in memory and flushed to disk every ~5 seconds (periodic tick) and on script unload/shutdown. There is no immediate flush on each storage.set() call.
-- Track cumulative kills across game sessions
function on_kill(player)
if not player:is_enemy() then return end
local total = (storage.get("total_kills") or 0) + 1
storage.set("total_kills", total)
print("Total kills: " .. total)
end
-- Store structured data as individual keys (no json global exists)
storage.set("loadout_slot1", "burst_fire")
storage.set("loadout_slot2", "trooper_bounty")
local slot1 = storage.get("loadout_slot1")
-- Delete a key
storage.set("old_data", nil)Helpers ​
Coroutine-friendly helpers available in every script covering common patterns: sleeping, debouncing, key management, and aim control. All timing helpers (sleep, wait_*) yield the coroutine and are only usable inside coroutine callbacks (e.g. on_tick).
Timing ​
| Function | Returns | Description |
|---|---|---|
sleep(ms) | - | Yield for approximately ms milliseconds |
debounce(key, cooldown_ms) | boolean | Return true at most once per cooldown_ms window for a given key |
function on_tick()
if not debounce("my_feature", 500) then return end
-- runs at most twice per second
endKey and Ability Shortcuts ​
| Function | Returns | Description |
|---|---|---|
press_ability(slot) | - | Press (down + up) the key bound to the given ability slot |
hold_key(key, duration?) | - | Hold a VK key for duration ms then release. Default 500 ms |
hold_ability(slot, duration?) | - | Hold the key bound to an ability slot. Default 500 ms |
press_ability(slot.ability1) -- tap ability 1
hold_key(VK.SPACE, 200) -- hold Space for 200 ms
hold_ability(slot.ability3, 300) -- hold ability 3 for 300 msAim Helpers ​
| Function | Returns | Description |
|---|---|---|
snap_to_target(target, opts?) | boolean | Flick aim to target using input.snap_to. Returns false if target is invalid or dead |
aim_at_target(target, opts?) | boolean | Smooth aim at target until within threshold FOV or timeout. Returns true on success |
lock_aim(target, opts?) | - | Start continuously aiming at target every tick (processed after on_tick) |
unlock_aim() | - | Cancel any active aim lock |
fire_and_hold(target, slot, opts?) | - | Press ability then continuously aim at target for hold_time ms |
opts table for aim helpers:
| Key | Type | Default | Notes |
|---|---|---|---|
bone | string | integer | spine_1 (index 4) for snap_to_target/aim_at_target; bone.chest for fire_and_hold | Bone to aim at |
projectile_velocity | number | 0 | Projectile speed for lead compensation |
hold_time | number | 200 | fire_and_hold only - ms to keep aiming after pressing |
speed | number | 0.5 | aim_at_target only - smoothing speed |
threshold | number | 0.5 | aim_at_target only - success FOV in degrees |
timeout | number | 2000 | aim_at_target only - max ms before returning false |
-- Flick and fire
if snap_to_target(target, { bone = bone.head }) then
press_ability(slot.ability1)
end
-- Smooth aim with timeout
if aim_at_target(target, { speed = 0.3, threshold = 1.5, timeout = 500 }) then
left_click()
end
-- Fire then hold aim on the target
fire_and_hold(target, slot.ability2, { hold_time = 300, bone = bone.chest })
-- Continuous tracking per tick
lock_aim(target, { bone = bone.chest })
-- later, when done:
unlock_aim()Wait Helpers ​
All wait_* helpers yield the coroutine until a condition is met, then return true. They return false if the timeout elapses first (default 3000 ms).
| Function | Returns | Description |
|---|---|---|
wait_for_cast_delay(slot, timeout?) | boolean | Yield until the ability's cast delay finishes |
wait_for_cooling_down(slot, timeout?) | boolean | Yield until the ability has STARTED cooling down (is_cooling_down becomes true). Returns true at that point, not when the cooldown finishes |
wait_until_in_range(target, distance, timeout?) | boolean | Yield until target is within distance metres |
function on_tick()
press_ability(slot.ability1)
if not wait_for_cast_delay(slot.ability1, 1000) then return end -- wait up to 1s
-- cast delay finished, ability is active
if not wait_for_cooling_down(slot.ability1, 5000) then return end -- wait for CD
-- ready again
endTargeting Helper ​
| Function | Returns | Description |
|---|---|---|
find_closest_by_predicate(predicate, max_dist) | player | nil | Find the closest enemy within max_dist metres that satisfies predicate(player) == true |
-- Find the closest enemy that has a specific item
local target = find_closest_by_predicate(function(p)
return p:has_item("phantom_strike") and p:is_alive()
end, 30.0)