Kobalt Lua API
This document describes the active Lua scripting API exposed by Kobalt when
LUA_SUPPORT_ENABLED is enabled.
Status after CS2 audit
• Runtime: Lua 5.4 through sol2.
• Script directory: %APPDATA%\Kobalt\scripts\.
• Main binding files: src/scripting/[Link], src/scripting/[Link],
src/scripting/LuaManager.h.
• CS2 architecture: player state is split between CCSPlayerController
and C_CSPlayerPawn; player APIs resolve controller indices to pawn han-
dles internally.
• Schema access: player, entity, weapon, movement, globals, and bones
reads use existing schema-backed wrappers and safe null checks.
• Clantag: scripts can set a custom static clantag instead of the built-in
Kobalt animation.
• Frame stages: on_frame_stage now dispatches and receives the integer
EClientFrameStage value.
• Disabled legacy block: the old disabled #if 0 weapon/convar/globals/bones
block is still present as dead code, but active implementations now exist
for weapon, cvars, globals, and bones.
Script metadata
Optional metadata can be placed at the top of a script:
-- @author YourName
-- @version 1.0
-- @description Short script description
Quick example
local white = { r = 255, g = 255, b = 255, a = 255 }
local red = { r = 255, g = 80, b = 80, a = 255 }
[Link]("on_paint", function()
if not engine.is_in_game() then
return
end
for _, idx in ipairs(entity.get_players()) do
if player.is_alive(idx) and player.is_enemy(idx) then
local x, y, z = player.get_origin(idx)
local ok, sx, sy = render.world_to_screen(x, y, z + 72)
if ok then
1
[Link](sx, sy, player.get_name(idx), red)
end
end
end
end)
client.log_success("Lua script loaded")
Clantag example
[Link]("MY TAG")
clantag.set_show_without_username(true)
client.log_success("Custom clantag enabled")
Return conventions
• Invalid index: most entity/player/weapon functions return -1 for invalid
entity handles.
• Missing numeric value: functions return 0 or 0.0 when no valid source
exists.
• Missing vectors/angles: functions return 0, 0, 0.
• Missing strings: functions return an empty string.
• Booleans: functions return false when unavailable.
Events
Register callbacks with:
[Link](event_name, callback)
[Link](event_name)
The alias API is also available:
environment.register_callback(event_name, callback)
environment.unregister_callback(event_name)
Event names
• on_paint / paint: called during render; use for drawing/UI.
• on_createmove / createmove: called from CreateMove; movement but-
ton edits are valid here.
• on_frame_stage / frame_stage: called after frame-stage post-original
work; callback receives stage.
• on_game_event / game_event: generic game event callback; receives
LuaGameEvent.
• on_player_hurt / player_hurt: receives LuaGameEvent for player hurt.
• on_player_death / player_death: receives LuaGameEvent for player
death.
2
• on_round_start / round_start: receives LuaGameEvent for round start.
• on_round_end / round_end: receives LuaGameEvent for round end.
• on_override_view / override_view: receives LuaViewSetup.
• on_unload / unload: called during script unload.
• on_player_chat / player_chat: registered name is exposed; dispatch
depends on hook coverage.
• on_console_input / console_input: registered name is exposed; dis-
patch depends on hook coverage.
• on_string_cmd / string_cmd: registered name is exposed; dispatch de-
pends on hook coverage.
Frame-stage constants
Use these with on_frame_stage or engine.get_frame_stage():
• engine.FRAME_NET_UPDATE_POSTDATAUPDATE_START
• engine.FRAME_NET_UPDATE_POSTDATAUPDATE_END
• engine.FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE
• engine.FRAME_RENDER_START
• engine.FRAME_RENDER_END
• engine.FRAME_NET_UPDATE_END
• engine.FRAME_NET_CREATION
• engine.FRAME_RESTORE_SERVER_STATE
• engine.FRAME_SIMULATE_END
Example:
[Link]("on_frame_stage", function(stage)
if stage == engine.FRAME_RENDER_START then
[Link]("render start")
end
end)
Types
LuaVec2
Fields:
• x: number
• y: number
LuaVec3
Fields:
• x: number
• y: number
• z: number
3
LuaAngle
Fields:
• pitch: number
• yaw: number
• roll: number
LuaColor
Fields are floats in the 0.0..1.0 range:
• r: number
• g: number
• b: number
• a: number
Color tables
Most older render functions accept integer color tables:
{ r = 255, g = 255, b = 255, a = 255 }
LuaTexture
Returned by texture setup functions.
Fields are internal; use with [Link].
LuaGameEvent
Methods:
• event:get_name() -> string
• event:get_bool(key, default?) -> bool
• event:get_int(key, default?) -> integer
• event:get_uint64(key, default?) -> integer
• event:get_float(key, default?) -> number
• event:get_string(key, default?) -> string
• event:set_bool(key, value)
• event:set_int(key, value)
• event:set_uint64(key, value)
• event:set_float(key, value)
• event:set_string(key, value)
LuaViewSetup
Methods:
• view:get_fov() -> number
• view:set_fov(value)
4
• view:get_viewmodel_fov() -> number
• view:set_viewmodel_fov(value)
• view:get_origin() -> x, y, z
• view:set_origin(x, y, z)
• view:get_angles() -> pitch, yaw, roll
• view:set_angles(pitch, yaw, roll)
LuaConVar
Returned by [Link](name) or [Link].
Methods:
• convar:valid() -> bool
• convar:get_name() -> string
• convar:get_string() -> string
• convar:get_float() -> number
• convar:get_int() -> integer
• convar:get_bool() -> bool
• convar:set_string(value) -> bool
• convar:set_float(value) -> bool
• convar:set_int(value) -> bool
• convar:set_bool(value) -> bool
client
• [Link](message): log info text to the Lua console.
• client.log_error(message): log error text.
• client.log_warning(message): log warning text.
• client.log_success(message): log success text.
• [Link](command): execute an unrestricted client command.
• client.get_time() -> number: ImGui time in seconds.
• client.get_frametime() -> number: ImGui frame delta.
• client.get_screen_size() -> width, height: current render-space
display size.
• client.play_sound(path, volume?) -> bool: play a .wav file from
disk or a VSound path.
clantag
The clantag API controls the same clantag/name-prefix path as the menu iden-
tity feature. [Link](text) enables the clantag changer and replaces the
default Kobalt animation with the supplied static text.
• [Link](text) -> bool: sanitize, truncate, enable, and apply a cus-
tom clantag. Returns false when the sanitized text is empty.
• [Link]() -> string: current custom clantag text.
5
• clantag.get_active() -> string: currently active tag frame being
sent/displayed.
• [Link](): clear custom text and disable the clantag changer.
• clantag.set_enabled(enabled): enable/disable the clantag changer.
• clantag.is_enabled() -> bool: current clantag changer state.
• clantag.set_show_without_username(enabled): when enabled, prefix
the clantag onto the real name if no custom username is set.
• clantag.get_show_without_username() -> bool
• clantag.MAX_LENGTH: maximum stored clantag length, currently 31.
engine
• engine.is_in_game() -> bool
• engine.is_connected() -> bool
• engine.get_max_clients() -> integer
• engine.get_local_player_index() -> integer: local controller entity
index.
• engine.get_view_angles() -> pitch, yaw, roll: current base com-
mand view angles.
• engine.get_tick_count() -> integer
• engine.execute_client_cmd(command)
• engine.chat_print(message)
• engine.get_level_name() -> string
• engine.camera_in_thirdperson() -> bool
• engine.get_netvar_offset(module, class, field) -> integer
• engine.get_frame_stage() -> integer: current frame stage during
frame-stage dispatch, otherwise -1.
entity
• entity.get_players() -> table<int>: controller entity indices.
• entity.get_highest_index() -> integer
• entity.get_origin(index) -> x, y, z
• entity.get_team(index) -> integer
• entity.get_health(index) -> integer
• entity.get_max_health(index) -> integer
• entity.get_by_index(index) -> integer: returns index if valid, else
-1.
• entity.get_class_name(index) -> string
• entity.is_valid(index) -> bool
entitylist
• entitylist.get_local_player_controller() -> integer
• entitylist.get_local_player_pawn() -> integer
• entitylist.get_game_rules() -> lightuserdata | nil
6
• entitylist.get_entities() -> table<int>
• entitylist.get_entities(callback)
• entitylist.get_entities(class_name, include_derived?) ->
table<int>
• entitylist.get_entities(class_name, include_derived?, callback)
Callback form avoids returning a full table:
entitylist.get_entities("C_CSPlayerPawn", true, function(index)
[Link]("pawn " .. tostring(index))
end)
player
All player functions take a controller entity index unless noted.
• player.get_name(index) -> string
• player.get_health(index) -> integer
• player.get_armor(index) -> integer
• player.has_helmet(index) -> bool
• player.has_defuser(index) -> bool
• player.get_team(index) -> integer
• player.is_alive(index) -> bool
• player.is_scoped(index) -> bool
• player.is_defusing(index) -> bool
• player.get_origin(index) -> x, y, z
• player.get_eye_pos(index) -> x, y, z
• player.get_velocity(index) -> x, y, z
• player.get_velocity_2d(index) -> number
• player.is_enemy(index) -> bool
• player.get_money(index) -> integer
• player.get_active_weapon(index) -> integer: active weapon entity
index.
• player.get_pawn(index) -> integer: pawn entity index for a
controller.
• player.get_eye_angles(index) -> pitch, yaw, roll
• player.get_local() -> integer: local controller index.
• player.get_local_pawn() -> integer: local pawn index.
weapon
Most weapon functions take a weapon entity index from player.get_active_weapon(index).
• weapon.get_clip1(weapon_index) -> integer
• weapon.get_name(weapon_index) -> string
• weapon.get_inaccuracy(weapon_index) -> number
• weapon.get_spread(weapon_index) -> number
• weapon.get_type(weapon_index) -> integer
7
• weapon.get_max_clip1(weapon_index) -> integer
• weapon.is_reloading(weapon_index) -> bool
• weapon.is_knife(weapon_index) -> bool
• weapon.is_grenade(weapon_index) -> bool
• weapon.get_damage(weapon_index) -> integer
• weapon.get_range(weapon_index) -> number
• weapon.get_penetration(weapon_index) -> number
Constants:
• weapon.TYPE_KNIFE
• weapon.TYPE_PISTOL
• weapon.TYPE_SMG
• weapon.TYPE_RIFLE
• weapon.TYPE_SHOTGUN
• weapon.TYPE_SNIPER
• weapon.TYPE_MG
• weapon.TYPE_GRENADE
• weapon.TYPE_C4
movement
Movement writes only make sense during on_createmove.
State reads:
• movement.is_on_ground() -> bool
• movement.is_ducking() -> bool
• movement.get_flags() -> integer
• movement.get_move_type() -> integer
• movement.get_velocity() -> x, y, z
• movement.get_speed() -> number
• movement.get_speed_3d() -> number
• movement.get_origin() -> x, y, z
Button reads/writes:
• movement.get_buttons() -> integer
• movement.is_button_pressed(flag) -> bool
• movement.set_button(flag, enabled)
• movement.set_buttons(buttons)
• [Link](enabled)
• [Link](enabled)
Flags:
• movement.FL_ONGROUND
• movement.FL_DUCKING
• movement.FL_WATERJUMP
• movement.FL_ONTRAIN
8
Command buttons:
• movement.IN_ATTACK
• movement.IN_JUMP
• movement.IN_DUCK
• movement.IN_FORWARD
• movement.IN_BACK
• movement.IN_USE
• movement.IN_MOVELEFT
• movement.IN_MOVERIGHT
• movement.IN_ATTACK2
• movement.IN_RELOAD
• movement.IN_SPRINT
• movement.IN_ZOOM
Move types:
• movement.MOVETYPE_NONE
• movement.MOVETYPE_WALK
• movement.MOVETYPE_FLY
• movement.MOVETYPE_FLYGRAVITY
• movement.MOVETYPE_NOCLIP
• movement.MOVETYPE_OBSERVER
• movement.MOVETYPE_LADDER
globals
• globals.get_tick_interval() -> number
• globals.get_tick_count() -> integer
• globals.get_curtime() -> number
• globals.get_realtime() -> number
• globals.get_frame_time() -> number
• globals.get_frame_count() -> integer
• globals.get_server_tick() -> integer
• globals.get_choked_commands() -> integer
• globals.get_max_clients() -> integer
• globals.get_map_name() -> string
• globals.is_freeze_time() -> bool
bones
All bones functions take a player controller index.
• bones.get_position(player_index, bone_index) -> x, y, z
• bones.get_position_by_name(player_index, bone_name) -> x, y,
z
• bones.get_head(player_index) -> x, y, z
• bones.get_count(player_index) -> integer
9
Constants:
• [Link]
• [Link]
• [Link]
• [Link]
• bones.LEFT_UPPER_ARM
• bones.LEFT_LOWER_ARM
• bones.LEFT_HAND
• bones.RIGHT_UPPER_ARM
• bones.RIGHT_LOWER_ARM
• bones.RIGHT_HAND
• bones.LEFT_UPPER_LEG
• bones.LEFT_LOWER_LEG
• bones.LEFT_ANKLE
• bones.RIGHT_UPPER_LEG
• bones.RIGHT_LOWER_LEG
• bones.RIGHT_ANKLE
render
These draw on the foreground draw list and generally take color tables.
• [Link](x, y, text, color)
• [Link](x, y, w, h, color, rounding?)
• render.rect_filled(x, y, w, h, color, rounding?)
• [Link](x1, y1, x2, y2, color, thickness?)
• [Link](x, y, radius, color, segments?)
• render.circle_filled(x, y, radius, color, segments?)
• render.world_to_screen(x, y, z) -> ok, sx, sy
• render.get_text_size(text) -> LuaVec2
• render.screen_size() -> LuaVec2
• render.setup_texture(path) -> LuaTexture
• render.setup_texture_from_memory(bytes) -> LuaTexture
• render.setup_texture_rgba(bytes, width, height) -> LuaTexture
• render.setup_texture_rgba(table, width, height) -> LuaTexture
• [Link](texture, x, y, w, h, color?)
• [Link](texture, pos, size, color?)
• render.rect_filled_fade(x, y, w, h, c1, c2, c3, c4)
• render.poly_line(points, color, closed?, thickness?)
• [Link](points, color)
draw
These draw on the background draw list and use integer color arguments.
• [Link](x1, y1, x2, y2, r, g, b, a?, thickness?)
10
• [Link](x, y, w, h, r, g, b, a?, rounding?, thickness?)
• draw.rect_filled(x, y, w, h, r, g, b, a?, rounding?)
• draw.rect_filled_multicolor(x, y, w, h, r1, g1, b1, a1, r2,
g2, b2, a2, r3, g3, b3, a3, r4, g4, b4, a4)
• [Link](x, y, radius, r, g, b, a?, segments?, thickness?)
• draw.circle_filled(x, y, radius, r, g, b, a?, segments?)
• [Link](x1, y1, x2, y2, x3, y3, r, g, b, a?, thickness?)
• draw.triangle_filled(x1, y1, x2, y2, x3, y3, r, g, b, a?)
• [Link](x1, y1, x2, y2, x3, y3, x4, y4, r, g, b, a?,
thickness?)
• draw.quad_filled(x1, y1, x2, y2, x3, y3, x4, y4, r, g, b,
a?)
• [Link](x, y, text, r, g, b, a?)
• draw.text_centered(x, y, text, r, g, b, a?)
• [Link](points, r, g, b, a?, closed?, thickness?)
• draw.convex_poly_filled(points, r, g, b, a?)
• draw.bezier_cubic(x1, y1, x2, y2, x3, y3, x4, y4, r, g, b,
a?, thickness?, segments?)
• draw.get_screen_size() -> width, height
• draw.world_to_screen(x, y, z) -> ok, sx, sy
input
• input.is_key_pressed(vk_code) -> bool: high bit from GetAsyncKeyState;
true while key is held.
• input.is_key_down(vk_code) -> bool: low bit from GetAsyncKeyState;
true on transition.
• input.get_mouse_pos() -> x, y: OS cursor position.
Constants include:
• input.VK_LBUTTON
• input.VK_RBUTTON
• input.VK_MBUTTON
• input.VK_XBUTTON1
• input.VK_XBUTTON2
• input.VK_SHIFT
• input.VK_CONTROL
• input.VK_MENU
• input.VK_SPACE
• input.VK_ESCAPE
• input.VK_INSERT
• input.VK_DELETE
• input.VK_HOME
• input.VK_END
• input.VK_TAB
• input.VK_RETURN
11
• input.VK_BACK
• input.VK_UP
• input.VK_DOWN
• input.VK_LEFT
• input.VK_RIGHT
• input.VK_F1 through input.VK_F12
math_ex
• math_ex.calc_angle(x1, y1, z1, x2, y2, z2) -> pitch, yaw
• math_ex.normalize_angle(angle) -> number
• math_ex.distance_3d(x1, y1, z1, x2, y2, z2) -> number
• math_ex.distance_2d(x1, y1, x2, y2) -> number
• math_ex.clamp(value, min, max) -> number
• math_ex.lerp(a, b, t) -> number
• math_ex.calc_fov(viewPitch, viewYaw, aimPitch, aimYaw) ->
number
• math_ex.calc_fov(viewPitch, viewYaw, viewRoll, aimPitch,
aimYaw, aimRoll) -> number
• math_ex.angle_vectors(pitch, yaw, roll?) -> forward, right,
up
• math_ex.vector_angles(x, y, z) -> pitch, yaw, roll
• math_ex.random_float(min, max) -> number
• math_ex.random_int(min, max) -> integer
config
Configuration access is by internal config variable name.
• config.get_bool(name) -> bool
• config.set_bool(name, value) -> bool
• config.get_float(name) -> number
• config.set_float(name, value) -> bool
• config.get_int(name) -> integer
• config.set_int(name, value) -> bool
• config.get_string(name) -> string
• config.set_string(name, value) -> bool
cvars
• [Link](name) -> LuaConVar
• [Link](name) -> bool
• cvars.<name> -> LuaConVar through metatable lookup.
Example:
local sv_cheats = [Link]("sv_cheats")
if sv_cheats:valid() then
12
[Link]("sv_cheats = " .. tostring(sv_cheats:get_int()))
end
environment
• environment.get_user_name() -> string
• environment.get_game_directory() -> string
• environment.get_script_name() -> string
• environment.color_print(message, color?)
• environment.find_pattern(module, pattern) -> lightuserdata |
nil
• environment.find_export(module, export_name) -> lightuserdata
| nil
• environment.register_callback(event_name, callback)
• environment.unregister_callback(event_name)
ui
The ui namespace wraps ImGui. Use from on_paint.
Windows
• ui.begin_window(title, closable?, flags?) -> visible, open
• ui.end_window()
• ui.set_next_window_pos(x, y, cond?)
• ui.set_next_window_size(w, h, cond?)
• ui.set_next_window_bg_alpha(alpha)
• ui.set_next_window_focus()
• ui.get_window_pos() -> x, y
• ui.get_window_size() -> w, h
• ui.get_content_region_avail() -> w, h
Child windows
• ui.begin_child(id, w?, h?, border?, flags?) -> bool
• ui.end_child()
Style
• ui.push_style_color(idx, r, g, b, a?)
• ui.pop_style_color(count?)
• ui.push_style_var(idx, value)
• ui.push_style_var2(idx, x, y)
• ui.pop_style_var(count?)
• ui.push_item_width(width)
• ui.pop_item_width()
• ui.push_font(font_index?)
13
• ui.pop_font()
Layout
• [Link]()
• [Link]()
• [Link](w, h)
• [Link](width?)
• [Link](width?)
• ui.same_line(offset?, spacing?)
• ui.new_line()
• ui.begin_group()
• ui.end_group()
• ui.set_cursor_pos(x, y)
• ui.set_cursor_pos_x(x)
• ui.set_cursor_pos_y(y)
• ui.get_cursor_pos() -> x, y
• ui.get_cursor_screen_pos() -> x, y
Text and controls
• [Link](text)
• ui.text_colored(text, r, g, b, a?)
• ui.text_disabled(text)
• ui.text_wrapped(text)
• ui.label_text(label, text)
• ui.bullet_text(text)
• [Link](label, w?, h?) -> bool
• ui.small_button(label) -> bool
• ui.invisible_button(id, w, h) -> bool
• ui.arrow_button(id, dir) -> bool
• [Link](label, value) -> bool
• ui.radio_button(label, active) -> bool
• ui.progress_bar(fraction, w?, h?, overlay?)
• [Link]()
Inputs
• ui.input_text(label, value, flags?) -> string
• ui.input_text_multiline(label, value, w?, h?) -> string
• ui.input_int(label, value) -> integer
• ui.input_float(label, value, format?) -> number
• ui.slider_int(label, value, min, max) -> integer
• ui.slider_float(label, value, min, max, format?) -> number
• ui.v_slider_int(label, w, h, value, min, max) -> integer
• ui.v_slider_float(label, w, h, value, min, max) -> number
• ui.drag_int(label, value, speed?, min?, max?) -> integer
14
• ui.drag_float(label, value, speed?, min?, max?) -> number
Color
• ui.color_edit3(label, r, g, b) -> table
• ui.color_edit4(label, r, g, b, a) -> table
• ui.color_picker(label, r, g, b, a) -> table
• ui.color_button(id, r, g, b, a?, w?, h?) -> bool
Combo/list/tree/tab/popup
• [Link](label, current, items, max_height?) -> integer
• [Link](label, current, items, height_items?) -> integer
• [Link](label, selected, flags?, w?, h?) -> bool
• ui.tree_node(label) -> bool
• ui.tree_node_ex(label, flags?) -> bool
• ui.tree_pop()
• ui.collapsing_header(label, flags?) -> bool
• ui.set_next_item_open(open, cond?)
• ui.begin_tab_bar(id, flags?) -> bool
• ui.end_tab_bar()
• ui.begin_tab_item(label, flags?) -> bool
• ui.end_tab_item()
• ui.open_popup(id)
• ui.begin_popup(id, flags?) -> bool
• ui.begin_popup_modal(name, flags?) -> bool
• ui.end_popup()
• ui.close_current_popup()
• ui.begin_popup_context_item(id?) -> bool
• ui.begin_popup_context_window(id?) -> bool
Tooltips and item state
• ui.begin_tooltip()
• ui.end_tooltip()
• ui.set_tooltip(text)
• [Link](text)
• ui.is_item_hovered(flags?) -> bool
• ui.is_item_active() -> bool
• ui.is_item_focused() -> bool
• ui.is_item_clicked(button?) -> bool
• ui.is_item_visible() -> bool
• ui.is_item_edited() -> bool
• ui.is_item_activated() -> bool
• ui.is_item_deactivated() -> bool
• ui.is_item_deactivated_after_edit() -> bool
• ui.is_any_item_hovered() -> bool
15
• ui.is_any_item_active() -> bool
• ui.is_any_item_focused() -> bool
• ui.get_item_rect_min() -> x, y
• ui.get_item_rect_max() -> x, y
• ui.get_item_rect_size() -> w, h
Scroll/focus/utilities
• ui.set_keyboard_focus_here(offset?)
• ui.set_scroll_here_x(ratio?)
• ui.set_scroll_here_y(ratio?)
• ui.get_scroll_x() -> number
• ui.get_scroll_y() -> number
• ui.get_scroll_max_x() -> number
• ui.get_scroll_max_y() -> number
• ui.set_scroll_x(value)
• ui.set_scroll_y(value)
• ui.push_id(id)
• ui.push_id_int(id)
• ui.pop_id()
• ui.get_id(id) -> integer
• ui.calc_text_size(text) -> w, h
• ui.get_frame_height() -> number
• ui.get_frame_height_with_spacing() -> number
• ui.get_text_line_height() -> number
• ui.get_text_line_height_with_spacing() -> number
• ui.get_time() -> number
• ui.get_frame_count() -> integer
• ui.get_io_display_size() -> w, h
Common UI constants
The namespace exposes ImGui window flags, tree flags, style colors, style vars,
condition flags, and directions. Examples:
• ui.WindowFlags_NoTitleBar
• ui.WindowFlags_NoResize
• ui.WindowFlags_AlwaysAutoResize
• ui.TreeNodeFlags_DefaultOpen
• ui.Col_Text
• ui.Col_WindowBg
• ui.StyleVar_Alpha
• ui.StyleVar_WindowRounding
• ui.Cond_Always
• ui.Cond_FirstUseEver
• ui.Dir_Left
• ui.Dir_Right
16
• ui.Dir_Up
• ui.Dir_Down
menu
• menu.set_background(path) -> bool
• menu.set_background_from_memory(bytes) -> bool
• menu.set_background_rgba(bytes, width, height) -> bool
• menu.set_background_gif(path) -> bool
• menu.set_background_gif_from_memory(bytes) -> bool
• menu.clear_background()
• menu.has_background() -> bool
• menu.set_background_alpha(alpha)
• menu.get_background_alpha() -> number
• menu.set_background_scale(mode): "fit", "fill", or "stretch".
• menu.get_background_size() -> width, height
• menu.is_open() -> bool
CS2-specific guidance
• Controller vs pawn: use entity.get_players() for controller indices,
then player.get_pawn(index) if you need the pawn entity index.
• Frame-stage work: run expensive model/entity queries on specific
stages, not every on_paint call.
• CreateMove work: only mutate movement buttons from on_createmove.
• Schema-backed reads: prefer exposed helpers over hardcoded offsets. If
you must use offsets, call engine.get_netvar_offset(module, class,
field) and handle 0 as failure.
• Rendering coordinates: screen size and world-to-screen are render-
space; this matches stretched-resolution handling.
Minimal ESP example
local function color(r, g, b, a)
return { r = r, g = g, b = b, a = a or 255 }
end
[Link]("paint", function()
local red = color(255, 60, 60)
for _, idx in ipairs(entity.get_players()) do
if player.is_alive(idx) and player.is_enemy(idx) then
local hx, hy, hz = bones.get_head(idx)
local ok, sx, sy = render.world_to_screen(hx, hy, hz + 8)
if ok then
local name = player.get_name(idx)
17
local hp = player.get_health(idx)
[Link](sx, sy, [Link]("%s [%d]", name, hp), red)
end
end
end
end)
Minimal frame-stage example
[Link]("frame_stage", function(stage)
if stage ~= engine.FRAME_RENDER_START then
return
end
local local_pawn = player.get_local_pawn()
if local_pawn ~= -1 then
[Link]("local pawn: " .. tostring(local_pawn))
end
end)
18