Scooby API Reference
TF2 Lua API
Retail & Classified Host API 1.0 · UI API 1.1
No matching API entries. Try tf2.entities or clear your search.
TF2 host API
Full Markdown referencetf2.info()Retail + Classified
# Permalink
-- -> {api_version, game_id, game, map, preview, sequence, generation,
-- engine_time, tick_interval}
local info=tf2.info()
print(info.game,info.map,info.api_version)
tf2.capabilities()Retail + Classified
# Permalink
-- -> {snapshots, projection, settings, shared_ui, four_teams,
-- civilian, mvm, viewmodel_offsets, native_hooks}
local caps=tf2.capabilities()
if caps.civilian then print(tf2.class_name(10)) end
tf2.session()Retail + Classified
# Permalink
-- -> {allowed, data_available, in_game, preview, stopped, menu_open, entity_count}
local state=tf2.session()
if state.data_available then print(state.entity_count) end
tf2.local_player()Retail + Classified
# Permalink
-- -> {handle, team, class_id, class_name, health, alive, on_ground, flags,
-- position, eye_position, velocity, view_angles, speed, sequence, generation} or nil
local me=tf2.local_player()
if me then print(me.class_name,me.health,me.speed) end
tf2.entities([filter])Retail + Classified
# Permalink
-- filter = {kind='all', relationship='all', max_distance=metres}
for _,entity in ipairs(tf2.entities{kind='player',relationship='enemy',max_distance=150}) do
print(entity.handle,entity.name,entity.distance_m)
end
tf2.entity(handle)Retail + Classified
# Permalink
-- Decimal string, opaque ID, or unsigned 32-bit full numeric handle -> entity or nil
local list=tf2.entities()
if list[1] then print(tf2.entity(list[1].handle).name) end
tf2.bones(handle)Retail + Classified
# Permalink
-- -> array of {from={x,y,z}, to={x,y,z}}
local entities=tf2.entities{kind='player'}
if entities[1] then print(#tf2.bones(entities[1].handle)) end
tf2.condition(handle, index)Retail + Classified
# Permalink
-- index 0..159 -> boolean; false for unavailable/non-player data
local entities=tf2.entities{kind='player'}
if entities[1] then print(tf2.condition(entities[1].handle,4)) end
tf2.camera()Retail + Classified
# Permalink
-- -> {position, angles, width, height} or nil
local camera=tf2.camera()
if camera then print(camera.width,camera.height) end
tf2.world_to_screen(position)Retail + Classified
# Permalink
-- Source {x,y,z} -> {x,y,on_screen} or nil
ui.overlay('tf2_player_labels',function()
for _,e in ipairs(tf2.entities{kind='player'}) do
local p=tf2.world_to_screen(e.position)
if p and p.on_screen then render.text(p.x,p.y,e.name,{1,1,1,1}) end
end
end)
tf2.screen_box(handle)Retail + Classified
# Permalink
-- -> {left,top,right,bottom,width,height,fitted} or nil
ui.overlay('tf2_box_example',function()
for _,e in ipairs(tf2.entities{kind='player',relationship='enemy'}) do
local b=tf2.screen_box(e.handle)
if b then render.rect(b.left,b.top,b.width,b.height,{.2,.8,1,1}) end
end
end)
tf2.setting(id, field [, value])Retail + Classified
# Permalink
-- field 'style' or 'distance' -> normalized numeric value
local old=tf2.setting('view.camera_fov','distance')
tf2.setting('view.camera_fov','distance',100)
features.set('view.camera_fov',true)
tf2.has_feature(id)Retail + Classified
# Permalink
-- -> boolean; useful for edition-specific controls
if tf2.has_feature('mvm.wave_panel') then features.set('mvm.wave_panel',true) end
tf2.keybind(id [, action [, mode]])Retail + Classified
# Permalink
-- -> key, mode, capturing, waiting
-- actions: 'capture', 'clear', 'mode'; modes: 0 Hold, 1 Toggle, 2 Always
local key,mode=tf2.keybind('aim.key')
print(key,mode)
tf2.object_preview(kind, chams)Retail + Classified
# Permalink
-- UI callback only. kind: "weapons", "health", "ammo", "items".
-- false uses the selected object's ESP settings; true uses chams.target.
ui.subtab("visuals", "custom.weapons", "Weapons", function()
tf2.object_preview("weapons", false)
end)
tf2.model_preview(chams)Retail + Classified
# Permalink
-- UI callback only; draws the product's actual class model preview
local tab=ui.tab('model_inspector','Model inspector')
ui.subtab(tab,'model','Model',function() tf2.model_preview(false) end)
tf2.class_name(id), tf2.team_name(id)Retail + Classified
# Permalink
print(tf2.class_name(1),tf2.team_name(2)) -- Scout, RED
tf2.vecRetail + Classified
# Permalink
local a={x=3,y=4,z=0}
local b={x=1,y=0,z=0}
local sum=tf2.vec.add(a,b)
local difference=tf2.vec.sub(a,b)
local scaled=tf2.vec.scale(a,2)
local dot=tf2.vec.dot(a,b)
local length=tf2.vec.length(a)
local distance=tf2.vec.distance(a,b)
local unit=tf2.vec.normalize(a) -- nil for near-zero length
tf2.angle_to(eye, point), tf2.angle_fov(from, to)Retail + Classified
# Permalink
local angles=tf2.angle_to({x=0,y=0,z=0},{x=100,y=0,z=50})
local degrees=tf2.angle_fov({x=0,y=0,z=0},angles)
tf2.to_metres(units), tf2.to_units(metres)Retail + Classified
# Permalink
local metres=tf2.to_metres(100)
print(metres,tf2.to_units(metres))
Built-in feature familiesRetail
# Permalink
| Family | IDs |
|---|---|
| Aim profiles | aim.*, aim.rage.*, combat.profile |
| Trigger profiles | trigger.*, trigger.rage.* |
| Movement/view | movement.bhop, movement.strafe, view.camera_fov, view.model_fov |
| Player ESP | esp.*, esp.teammate.* |
| Item ESP | esp.world.health.*, esp.world.ammo.*, esp.world.weapons.* |
| Chams targets | chams.players, teammates, self, arms, weapon, world_weapon, buildings, items, dropped_weapons |
Classified additions & overrides
Full Markdown referenceBuilt-in feature familiesClassified
# Permalink
| Family | IDs |
|---|---|
| Aim profiles | aim.*, aim.rage.*, combat.profile |
| Trigger profiles | trigger.*, trigger.rage.* |
| Movement/view | movement.bhop, movement.strafe, view.camera_fov, view.model_fov |
| Player ESP | esp.*, esp.teammate.* |
| Item ESP | esp.world.health.*, esp.world.ammo.*, esp.world.weapons.* |
| Class skins | skins.enabled, skins.class, skins.classN.player_NAME, skins.classN.weapon_ITEMID |
| Chams targets | chams.players, teammates, self, arms, weapon, world_weapon, buildings, items, dropped_weapons |
tf2.skins_page(players)Classified
# Permalink
local tab=ui.tab("custom_skins","Skins")
ui.subtab(tab,"weapons","Weapons",function() tf2.skins_page(false) end)
ui.subtab(tab,"players","Players",function() tf2.skins_page(true) end)
Shared UI API
Full Markdown referenceGetting startedShared UI
# Permalink
local enabled = features.add {
id = "enabled", label = "My feature", default = false,
category = "My Tools", description = "My custom feature", key = "F8"
}
local tab = ui.tab("tools", "My Tools")
ui.subtab(tab, "general", "General", function()
ui.group("Options", function()
ui.feature(enabled)
end)
end)
LocalizationShared UI
# Permalink
ui.subtab("settings", "localized_stats", "Info", function()
imgui.text(string.format(ui.tr("Speed: %.0f units/s"), 250))
end)
Lua console diagnosticsShared UI
# Permalink
[ERROR] [Lua error] My overlay | events.update (runtime)
My overlay.lua:12: attempt to index a nil value
stack traceback:
My overlay.lua:12: in local 'read_player'
My overlay.lua:20: in function <My overlay.lua:19>
This callback was disabled. Fix the error and run the script again.
Register a featureShared UI
# Permalink
local id = features.add {
id = "example", -- required local ID, 1-80 characters
label = "Example", -- defaults to local ID
category = "My Tools / General", -- defaults to "Scripts"
description = "What this does",
default = false, -- initial enabled state
key = "F8", -- optional key, Toggle mode
active_list = true, -- include effective toggles in Active Features
kind = "toggle" -- "toggle" or "action"
}
local action = features.add {
id = "refresh", label = "Refresh", kind = "action",
on_trigger = function() print("Refresh requested") end
}
Read and change featuresShared UI
# Permalink
| Function | Behavior |
|---|---|
features.get(id) |
Returns the saved enabled boolean, or nil for an unknown ID. |
features.set(id, boolean) |
Changes enabled state; rejects unknown IDs. |
features.active(id) |
Returns effective hotkey state, including Hold/Hold Off. |
features.trigger(id) |
Activates an action, including its callback and overlay flash. |
features.bind(id, key, mode) |
Sets a key and mode. Empty key clears the key. |
features.color(id, r, g, b, a) |
Sets RGBA color; components must be 0-1. Alpha defaults to 1. |
features.list() |
Returns an array of tables with id, label, category, description, kind, enabled. |
ui.feature(id) |
Draws the standard feature row, including applicable gear/color/hotkey controls. |
ui.keybind(id [, label]) |
Draws shared capture, Clear and activation mode for an existing feature; drawing callbacks only. Label defaults to Key. |
Independent health overlaysShared UI
# Permalink
features.set("esp.health", true)
features.set("esp.health_text", true)
features.bind("esp.health_text", "F7", "toggle")
ui.tab("health_overlays", "Health", function()
ui.feature("esp.health")
ui.feature("esp.health_text")
end)
Inline key captureShared UI
# Permalink
ui.tab("aim_controls", "Aimbot", function()
ui.group("Activation", function()
ui.feature("host.aim")
ui.keybind("host.aim", "Aim key")
end)
end)
Tabs and sub-tabsShared UI
# Permalink
local tab = ui.tab("tools", "My Tools")
local subtab = ui.subtab(tab, "display", "Display", function()
imgui.text("My page")
end)
ui.subtab("settings", "my_settings", "My Script", function()
imgui.text("An extra page beside the built-in settings.")
end)
Groups and columnsShared UI
# Permalink
ui.columns(3, function()
ui.group("First", function() imgui.text("First column") end)
ui.next_column()
ui.group("Second", function() imgui.text("Second column") end)
ui.next_column()
ui.group("Third", function() imgui.text("Third column") end)
end)
ImGui controlsShared UI
# Permalink
local amount, enabled, text = 50, false, "Hello"
ui.window("example", "Controls", {}, function()
local changed
changed, enabled = imgui.checkbox("Enabled", enabled)
changed, amount = imgui.slider_float("Amount", amount, 0, 100)
changed, text = imgui.input_text("Text", text)
end)
| Function | Return / behavior |
|---|---|
imgui.text(text) |
Unwrapped text; treats the string as text, not a printf format. |
imgui.text_colored(text, rgba) |
Colored text. |
imgui.button(label [, width, height]) |
Returns true on activation; dimensions default to automatic. |
ui.selectable(label, selected [, width, height]) |
Plain text row using the host selection highlight. Returns true on mouse activation; selection remains caller-owned. Zero dimensions use available width and text height. |
imgui.checkbox(label, value) |
Returns changed, boolean. |
imgui.slider_float(label, value, min, max[, options]) |
Returns changed, number. Optional {style="track"} uses a slim track and separate value. |
imgui.slider_int(label, value, min, max[, options]) |
Returns changed, integer-valued number. Optional {style="track"} uses a slim track and separate value. |
imgui.input_text(label, text) |
Returns changed, string; maximum 4095 bytes. |
imgui.combo(label, index, items) |
Returns changed, selected index. Indices start at 1; 1-128 items. |
imgui.color_edit(label, rgba) |
Returns changed, RGBA table. |
imgui.same_line([spacing, local_x]) |
Places the next item on the same line; default style spacing. |
imgui.separator() |
Separator line. |
imgui.spacing([height]) |
Vertical space; default 4 physical pixels. |
imgui.tooltip(text) |
Tooltip when the preceding item is hovered. |
imgui.progress(fraction) |
Progress bar; fraction is clamped to 0-1. |
imgui.available() |
Returns available content width, height. |
imgui.cursor() |
Returns cursor x, y in screen coordinates. |
imgui.set_cursor(x, y) |
Sets the next cursor position in window-local coordinates. |
imgui.is_item_hovered() |
Returns whether the preceding item is hovered. |
imgui.child(id, width, height, draw [, options]) |
Scoped scrollable child; 0 uses remaining size. Options: border, padding (retain WindowPadding without a visible border), horizontal_scroll. |
imgui.disabled(boolean, draw) |
Scoped disabled controls. |
imgui.with_style(colors, draw) |
Scoped color overrides, restored even after callback errors. |
imgui.window(title, options, draw) |
Scoped independent ImGui window. |
Selectable catalog rowsShared UI
# Permalink
local selected = 1
local catalog = {"Crowbar", "Health kit"}
ui.tab("catalog", "Spawner", function()
ui.group("Items", function()
for index, label in ipairs(catalog) do
if ui.selectable(label .. "##item_" .. index, selected == index) then
selected = index
end
end
imgui.disabled(true, function()
ui.selectable("Unavailable item", false)
end)
end)
if ui.button("Spawn item") then
print("Spawn requested for " .. catalog[selected])
-- Invoke the host's actual spawn API here.
end
end)
Overlays and drawingShared UI
# Permalink
ui.overlay("status", function()
local width, height = engine.viewport()
render.rect(20, height - 60, 240, 36, {0.05, 0.07, 0.09, 0.9}, true, 4)
render.text(30, height - 51, "Lua overlay", {0.4, 0.8, 1, 1}, 14)
end)
| Function | Arguments |
|---|---|
render.text(x, y, text, rgba [, size]) |
Text; size defaults to 13 and is limited to 8-80. |
render.line(x1, y1, x2, y2, rgba [, thickness]) |
Line; default thickness 1. |
render.rect(x, y, width, height, rgba [, filled, rounding]) |
Filled or outlined rectangle; default outline, rounding 0. |
render.circle(x, y, radius, rgba [, filled, thickness]) |
Filled or outlined circle; default outline, thickness 1. |
Themes and UI overridesShared UI
# Permalink
ui.theme {
rounding = 5,
colors = {
WindowBg = {0.06, 0.07, 0.1, 1},
Text = {0.9, 0.92, 1, 1},
CheckMark = {0.75, 0.4, 1, 1},
SliderGrab = {0.75, 0.4, 1, 1},
Button = {0.22, 0.15, 0.32, 1},
ButtonHovered = {0.32, 0.22, 0.45, 1}
}
}
ui.override("settings/interface", function()
imgui.text("My replacement settings page")
if imgui.button("Log") then print("Replacement works") end
end)
Events and host informationShared UI
# Permalink
events.on("update", function()
-- Read feature state and call a project-provided API here.
end)
events.on("shutdown", function()
print("Cleaning up my script")
end)
| Function | Return |
|---|---|
engine.time() |
ImGui elapsed time in seconds. |
engine.delta_time() |
Frame delta in seconds. |
engine.fps() |
ImGui's smoothed frame rate. |
engine.viewport() |
Host viewport width, height. |
print(...), base.log(...) |
Write to the shared console. |
Porting and game-specific APIsShared UI
# Permalink
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
app.scripts().registerHostApi = [](lua_State* L) {
lua_newtable(L);
lua_pushcfunction(L, [](lua_State* state) -> int {
lua_pushstring(state, "My Game");
return 1;
});
lua_setfield(L, -2, "name");
lua_setglobal(L, "game");
};
app.scripts().run(source, app.features(), logCallback, "My Script");
auto names = app.scripts().running();
app.scripts().stop("My Script");
app.scripts().stopAll();
app.navigateScript("lua:My Script:tools", "lua:My Script:general");
app.setApiDocsVisible(true, false); // true = attach left, false = attach right
Custom product controlsShared UI
# Permalink
local values = {features.get("aim.common"), features.get("aim.special")}
local changed, selected = ui.multi_combo("Targets", values, {"Common infected", "Special infected"})
if changed then
features.set("aim.common", selected[1])
features.set("aim.special", selected[2])
end
Entity colorsShared UI
# Permalink
| Signature | Behavior |
|---|---|
esp_colors.available() |
Returns whether the host registered classification color support. |
esp_colors.categories() |
Returns the 13 stable category IDs in display order. |
esp_colors.enabled([boolean]) |
Reads or sets the category master. Unsupported hosts return false; writes fail. |
esp_colors.get(category) |
Returns enabled, color, second_color and tint_fill; returns nil without host support. |
esp_colors.set(category, settings) |
Atomically applies supplied fields. RGBA arrays require exactly four finite numbers from 0 to 1. |
esp_colors.reset([category]) |
Resets one category, or all categories and the master when omitted. |
ui.entity_colors() |
Draws the category dropdown inline inside a Filters group. Non-Global categories expose Override global; enabled overrides show a color picker and settings popup. Returns false without host support. |
if esp_colors.available() then
esp_colors.set("npc_friendly", {
enabled = true,
color = {0.2, 0.9, 0.4, 1},
second_color = {0.1, 0.5, 0.2, 1},
tint_fill = false
})
ui.window("entity_colors", "Entity colors", function()
ui.group("Filters", function() ui.entity_colors() end)
end)
end
Native controlsShared UI
# Permalink
local changed, enabled = imgui.checkbox("Enabled", enabled)
imgui.set_next_item_width(140)
changed, amount = imgui.slider_float("Amount", amount, 0, 1)
changed, count = imgui.slider_int("Count", count, 0, 10)
changed, choice = imgui.combo("Mode", choice, {"First", "Second"})
changed, amount = imgui.drag_float("Drag", amount, 0, 1, 0.01)
changed, count = imgui.drag_int("Count", count, 0, 100, 1)
if imgui.radio_button("Mode A", choice == 1) then choice = 1 end
imgui.text_wrapped("A longer description that wraps within the current layout.")
Themed widgetsShared UI
# Permalink
-- widgets and ui.widgets are the same table. Legacy ui.* names remain compatible.
local changed, enabled = widgets.toggle("Enabled", enabled)
changed, amount = widgets.slider("Amount", amount, 0, 1)
changed, count = widgets.slider_int("Count", count, 0, 10)
changed, choice = widgets.combo("Mode", choice, {"First", "Second"})
changed, selected = widgets.multi_combo("Targets", selected, {"One", "Two"})
if widgets.button("Apply") then print("Applied") end
widgets.columns(2, function()
widgets.group("First", function() widgets.toggle("Example", false) end)
widgets.next_column()
widgets.group("Second", function() imgui.text("Shared theme") end)
end)
imgui.table and columnsShared UI
# Permalink
imgui.table("layout", 2, {borders=false, resizable=true, row_bg=false,
width=0, height=0, scroll_y=false}, function()
-- Width is a stretch weight unless fixed=true. Setup precedes any rows.
imgui.table_setup_column("Controls", 1, false)
imgui.table_setup_column("Preview", 1, false)
imgui.table_headers_row()
imgui.table_next_row(24) -- optional minimum row height
imgui.table_next_column()
imgui.text("Column one")
imgui.table_set_column(2) -- one-based
imgui.child("preview", 0, 100, function() imgui.text("Column two") end,
{border=true, horizontal_scroll=true})
end)
Groups, IDs and placementShared UI
# Permalink
imgui.with_id("unique_scope", function()
imgui.group(function()
local x,y = imgui.cursor_local()
imgui.set_cursor(x+8,y)
imgui.button("Repeated label")
imgui.same_line(8)
imgui.dummy(12,24)
end)
end)
local screenX,screenY = imgui.cursor()
imgui.set_cursor_screen(screenX,screenY)
local width,height = imgui.available()
local windowX,windowY = imgui.window_pos()
local windowWidth,windowHeight = imgui.window_size()
local textWidth,textHeight = imgui.text_size("Measure me")
imgui.with_font_sizeShared UI
# Permalink
imgui.with_font_size(14, function()
imgui.text("Readable standalone text")
imgui.checkbox("Enabled", true)
end)
imgui.with_style_varsShared UI
# Permalink
imgui.with_style_vars({WindowPadding={8,8},FramePadding={4,2},ItemSpacing={5,3},
CellPadding={4,3},WindowRounding=3,FrameRounding=0,GrabMinSize=9},function()
imgui.button("Compact")
end)
Tabs and treesShared UI
# Permalink
imgui.tab_bar("pages",function()
imgui.tab_item("Main",function() imgui.text("Main page") end)
imgui.tab_item("Settings",function() imgui.text("Settings page") end)
end)
imgui.tree("Details",function() imgui.text("Expanded details") end)
Hit targets and inputShared UI
# Permalink
local x,y = imgui.cursor()
if imgui.invisible_button("custom_toggle", 120, 24) then enabled = not enabled end
local hovered,active = imgui.is_item_hovered(),imgui.is_item_active()
local x1,y1,x2,y2 = imgui.item_rect()
local mouseX,mouseY = imgui.mouse_pos()
local deltaX,deltaY = imgui.mouse_delta()
local down = imgui.is_mouse_down(0) -- 0 left, 1 right, 2 middle, 3/4 extra
local clicked,released = imgui.is_mouse_clicked(0),imgui.is_mouse_released(0)
local itemClicked = imgui.is_item_clicked(0)
draw.rect(x,y+4,30,16,enabled and {0.5,0.8,0.2,1} or {0.2,0.2,0.2,1},true,8)
draw.circle(x+(enabled and 22 or 8),y+12,5,{1,1,1,1},true)
draw.text(x+40,y+5,"Custom toggle",{1,1,1,1},13)
Window drawing and clippingShared UI
# Permalink
-- Screen coordinates. draw.* uses the current window's draw list and clipping.
-- render.* retains the existing foreground overlay behavior.
draw.text(x,y,"Label",{1,1,1,1},13)
draw.line(x,y,x+100,y,{0.5,0.8,0.2,1},2)
draw.rect(x,y,100,30,{0.1,0.1,0.1,1},true,4)
draw.circle(x+12,y+12,8,{1,1,1,1},false,2)
draw.triangle(x,y,x+12,y,x+6,y+8,{1,1,1,1},true,1)
imgui.with_clip_rect(x,y,x+100,y+30,function()
draw.text(x,y,"Clipped content",{1,1,1,1},13)
end)
Custom dropdowns and popupsShared UI
# Permalink
if imgui.button("Custom dropdown") then imgui.open_popup("choices") end
imgui.popup("choices",function()
if imgui.selectable("First",choice==1) then choice=1 end
if imgui.selectable("Second",choice==2) then choice=2 end
-- selectable(label, selected [, width, height, keep_open])
-- With keep_open=true, Lua can build a multi-select list.
end)
imgui.combo_custom("Custom combo",tostring(choice),function()
if imgui.button("Use first") then choice=1;imgui.close_popup() end
end)
Slim slider appearanceShared UI
# Permalink
imgui.set_next_item_width(170)
changed, amount = imgui.slider_float("Amount", amount, 0, 1, {style="track"})
changed, count = imgui.slider_int("Count", count, 0, 100, {style="track"})
-- Default / {style="native"}: standard Dear ImGui slider.
-- Track: drag to adjust; click the value / Ctrl-click to type.
-- Arrow adjustment is available when the host enables ImGui keyboard navigation.
-- Track numeric entry clamps to the supplied range. Colors and sizing use ImGui style.
Compact example GUIShared UI
# Permalink
-- Run ImGui Demo.lua from Lua > Scripts; F10 toggles visibility.
-- Main: native ImGui widgets aligned in titled panels and responsive columns.
-- Visuals: Lua-drawn toggles, slider and dropdown contents with a live local preview.
-- Settings: accent/footer preferences and optional widgets.* controls.