Lua API — destahook
Версия API: 1 · Lua 5.4.9 · CS2 Dev, 14.10.2026
Быстрый старт · gamesensical read-only · Blueprint Editor
Каждый .lua-файл получает собственное Lua-состояние: глобальные переменные разных скриптов не пересекаются. Код файла выполняется при загрузке, а дальнейшая работа происходит через events.on. Колбэки вызываются без аргументов; данные берутся через таблицы API.
Жизненный цикл
script = { name = "My overlay", author = "you", version = "1.0" }
events.on("tick", function() end) -- логика кадра
events.on("render", function() end) -- фоновые примитивы оверлея
events.on("menu", function() end) -- только когда открыта вкладка Lua
events.on("shutdown", function() end) -- перед выгрузкой или reload
events.on(name, callback) возвращает true для поддерживаемых событий и false для неизвестного имени или невалидной функции. Несколько колбэков одного события исполняются в порядке регистрации. В обычном кадре сначала строится меню, затем вызываются tick и render. Даже вне матча последние два события вызываются, но entity.in_game() будет false.
Скрипт с ошибкой показывает её в менеджере и пишет в лог. Вызовы Lua защищены; действует лимит примерно 250 000 инструкций на загрузку/колбэк. После reload состояние скрипта создаётся заново. Ошибку исправляют через Edit source → Save, затем нажимают Reload; новые ревизии облачной библиотеки также отслеживаются автоматически.
Типы
| Тип | Создание | Поля и методы |
|---|---|---|
Vec2 | Vec2.new(x, y) | x, y, :length(), :distance_to(other) |
Vec3 | Vec3.new(x, y, z) | x, y, z, :length(), :distance_to(other), :distance_2d(other) |
Color | Color.new(r, g, b, a) или render.color(...) | r, g, b, a — целые 0–255 |
Entity | Возвращается entity.* | Снимок данных игрока, не изменяет память игры |
У render.color(r, g, b, a?) параметр a по умолчанию равен 255. Цветовые компоненты при отрисовке ограничиваются диапазоном 0–255.
render — отрисовка
Все координаты для 2D-примитивов — пиксели экрана. Примитивы добавляются в фоновый draw list текущего кадра; их удобно вызывать из события render.
| Вызов | Что делает |
|---|---|
render.screen_size() → Vec2 | Размер экрана оверлея |
render.world_to_screen(Vec3) → visible, Vec2 | Проекция мировой точки на экран; рисуйте её только при visible == true |
render.line(x1, y1, x2, y2, color, thickness?) | Линия; толщина по умолчанию 1 |
render.rect(x, y, width, height, color, rounding?, thickness?) | Контур прямоугольника |
render.rect_filled(x, y, width, height, color, rounding?) | Заполненный прямоугольник |
render.circle(x, y, radius, color, thickness?, segments?) | Контур окружности |
render.circle_filled(x, y, radius, color, segments?) | Заполненная окружность |
render.text(x, y, text, color, size?, outline?) | Текст; outline=true добавляет тёмную обводку |
render.measure_text(text, size?) → Vec2 | Ширина и высота текста для заданного шрифта |
render.draggable_text(id, x, y, text, color, size?, outline?, relative?) → Vec2 | Текст с сохраняемой перемещаемой позицией; relative по умолчанию true |
size по умолчанию берётся из текущего шрифта. Необязательные параметры можно опускать только справа.
events.on("render", function()
local size = render.screen_size()
local red = render.color(255, 80, 80)
render.line(size.x / 2 - 8, size.y / 2,
size.x / 2 + 8, size.y / 2, red, 2)
end)
layout — перемещаемые элементы
Переключатель Edit overlay positions на вкладке Lua и в окне Blueprint показывает рамки элементов, созданных через render.draggable_text или layout.position. При открытом меню перетащите рамку мышью; если окно редактора её закрывает, отодвиньте или закройте окно. Координаты автоматически сохраняются в облаке после отпускания кнопки в профиле конкретного скрипта и восстанавливаются после reload. Для каждого элемента в Overlay positions можно переключить Scale with screen или нажать Reset.
| Вызов | Результат |
|---|---|
layout.position(id, defaultX, defaultY, width, height, relative?) → Vec2 | Текущая позиция прямоугольной области; id должен быть уникален внутри скрипта |
layout.save() → boolean | Принудительно сохранить позиции; обычно не требуется |
layout.set_relative(id, enabled) → boolean | Переключить режим для уже созданного элемента, сохранив его видимое место |
layout.reset(id) → boolean | Удалить сохранённую позицию; при следующем вызове применятся значения по умолчанию |
width и height задают размер области захвата мышью, не рисуют прямоугольник. В режиме relative=true сохраняется доля доступной области экрана, поэтому элемент остаётся примерно на том же месте при смене разрешения. При false сохраняются пиксели; позиция ограничивается границами экрана. Параметр relative задаёт режим при первом появлении ID; затем действует сохранённый режим до layout.set_relative или Reset. Максимальная длина ID — 128 символов.
events.on("render", function()
local pos = layout.position("my_box", 40, 80, 180, 50, true)
render.rect(pos.x, pos.y, 180, 50, render.color(80, 220, 150), 4, 2)
end)
Для текста проще использовать render.draggable_text: он сам измерит надпись и нарисует её на сохранённой позиции.
math — числовые функции
Стандартная Lua-библиотека math дополнена следующими функциями:
| Вызов | Результат |
|---|---|
math.clamp(value, min, max) | Ограничение диапазоном; перепутанные границы меняются местами |
math.lerp(from, to, amount) | Линейная интерполяция; amount не ограничивается |
math.remap(value, inMin, inMax, outMin, outMax) | Перенос числа между диапазонами; при равных входных границах вернёт outMin |
math.smoothstep(edge0, edge1, value) | Плавный переход от 0 до 1 |
math.round(value) | Округление до ближайшего целого значения |
math.distance2d(x1, y1, x2, y2) | Расстояние на плоскости |
math.distance3d(x1, y1, z1, x2, y2, z2) | Расстояние в 3D |
thread — ожидание без блокировки кадров
thread.create(function() ... end) запускает задачу на Lua-корутине и возвращает её числовой ID. Внутри такой задачи thread.wait(milliseconds) уступает выполнение до следующего подходящего кадра. thread.cancel(id) отменяет ожидающую задачу и возвращает true, если она существовала. В одном скрипте допускается до 64 задач. Это не поток ОС: все продолжения исполняются последовательно вместе с Lua-событиями, а фактическая пауза может быть чуть дольше указанной из-за частоты кадров. Вызов thread.wait вне thread.create является ошибкой.
events.on("tick", function()
if not timer_started then
timer_started = true
thread.create(function()
thread.wait(500)
client.log("Прошло не менее 500 мс")
end)
end
end)
Обычные элементы ui.* вызывайте из menu, а отрисовку render.* — из render: корутина предназначена для отложенной логики, не для самостоятельной отрисовки.
entity — игровой контекст
| Вызов | Результат |
|---|---|
entity.in_game() | true, когда в текущем кадре доступен local player |
entity.local_player(with_bones?) | Entity локального игрока; вне игры valid=false |
entity.players(with_bones?) | Lua-массив снимков остальных игроков; может включать умерших |
entity.by_id(id, with_bones?) | Entity по индексу 1–63 или невалидный Entity |
Поля Entity: valid, id, pawn_address, controller_address, name, weapon, health, armor, flags, on_ground, team, ping, ammo, max_ammo, distance, velocity, alive, scoped, reloading, has_helmet, origin (Vec3), absolute_velocity (Vec3), bone_count, :has_bone(index), :bone(index) (Vec3).
health и armor — целые значения, flags — целая битовая маска состояния, on_ground — готовый boolean-флаг «стоит на земле» (первый бит flags). Для простой проверки прыжка удобнее on_ground, чем ручная работа с битами.
with_bones по умолчанию false: это экономит чтения памяти и FPS. Перед работой с костями вызывайте entity.local_player(true), entity.players(true) или entity.by_id(id, true). Первый такой вызов может вернуть пустой список/незаполненные кости: чтение включается со следующего кадра. Индексы костей с нуля. Перед :bone(index) проверяйте :has_bone(index). distance — расстояние в игровых единицах, не метры. Снимки отражают прочитанное состояние текущего кадра; не храните их как «живые» объекты для будущих кадров.
events.on("render", function()
if not entity.in_game() then return end
for _, player in ipairs(entity.players(true)) do
if player.valid and player.alive and player:has_bone(7) then
local on_screen, head = render.world_to_screen(player:bone(7))
if on_screen then
render.text(head.x, head.y, player.name,
render.color(255, 255, 255), 14, true)
end
end
end
end)
schema — чтение runtime schema
Этот API только читает память игры. Он не записывает значения и не создаёт игровые объекты.
| Вызов | Результат |
|---|---|
schema.ready() | Готова ли runtime schema |
schema.offset(class_name, field_name, module?) | Смещение поля или nil |
schema.read_int(address, class_name, field_name) | Целое или nil |
schema.read_uint64(address, class_name, field_name) | 64-битное целое (в том числе указатель) или nil |
schema.read_float(address, class_name, field_name) | Число или nil |
schema.read_bool(address, class_name, field_name) | Boolean или nil |
schema.read_vec2(address, class_name, field_name) | Vec2 или nil |
schema.read_vec3(address, class_name, field_name) | Vec3 или nil |
address — адрес базового объекта, например player.pawn_address; чтение производится по адресу address + найденное_смещение. Первые два строковых параметра должны совпадать с именами класса и поля в runtime schema. Третий параметр module поддерживается у schema.offset, если нужно уточнить модуль. Неизвестное поле, неготовая schema или неудачное чтение дают nil.
local local_player = entity.local_player()
if local_player.valid and schema.ready() then
local value = schema.read_int(local_player.pawn_address,
"C_CSPlayerPawn", "m_iHealth")
if value ~= nil then print("health", value) end
end
Названия schema-полей зависят от текущей версии игры. Не выполняйте десятки сырых чтений каждый кадр без необходимости.
config — настройки приложения
config.get(key) возвращает значение или nil для неизвестного ключа. config.set(key, value) возвращает true при успехе и false для неизвестного, read-only или неверно типизированного значения. config.keys() возвращает отсортированный Lua-массив доступных ключей. config.describe(key) возвращает сведения о типе ключа или nil для неизвестного ключа: type (boolean, integer, number, string, color), read_only, keybind; для бинда также key_code и key_name.
print(config.get("aim.fov"))
config.set("aim.fov", 7.5)
config.set("esp.box_color", render.color(255, 120, 60, 255))
local info = config.describe("aim.key")
if info and info.keybind then print(info.key_name, info.key_code) end
Внизу вкладки Lua раскройте Developer mode: Lua config keys: там можно найти ключ, скопировать точный путь кнопкой Copy, увидеть его тип и текущую клавишу бинда. Это избавляет от угадывания имени поля при работе с config.* и нодами Blueprint.
Доступные ключи сгруппированы ниже. Типы: bool, int, float, string, Color.
| Префикс | Ключи |
|---|---|
aim. | autowall, autowall_key, draw_fov, enabled, fov, hitbox_mask, key, minimum_damage, minimum_damage_key, minimum_damage_override, smoothing, visible_only |
autofire. | autowall, autowall_minimum_damage, before_delay, between_delay, enabled, hitchance, key, key_mode, minimum_damage |
esp. | bone_color, bones, box_color, box_length, box_style, box_thickness, c4, c4_color, chams_only_visible, enabled, flag_armor, flag_distance, flag_lethal, flag_ping, flags, glow_color, head, head_color, health_bar, health_number, history_chams, history_color, history_seconds, history_skeleton, local_viewmodel_chams, local_viewmodel_color, local_weapon_chams, local_weapon_color, name, offscreen, offscreen_color, shot_chams, shot_color, shot_skeleton, sound, sound_color, sound_distance, sound_enemies_only, sound_label, sound_lifetime, sound_radius, sound_thickness, team_check, visible_only, weapon |
game. | map |
menu. | frame_limit, frame_limit_enabled, renderer, vsync |
misc. | anti_flash, anti_smoke, bomb_timer, damage_log, dropped_glow, dropped_glow_color, grenade_warning, hitmarker, hotkey_list, penetration_crosshair, penetration_crosshair_mode, sniper_crosshair, spectator_avatars, spectator_color, spectator_list, spectator_warning, velocity, watermark, watermark_style |
trigger. | autowall, autowall_minimum_damage, before_delay, between_delay, enabled, hitchance, key, minimum_damage, prediction, prediction_seconds, seeded, seeded_key, team_check |
walkbot. | auto_buy, auto_reconnect, debug, enabled, smoothing, use_aim_trigger |
Точный тип возвращает config.describe(key). config.set меняет настройки самого оверлея, а не память CS2. trigger.prediction_seconds задаётся в секундах и ограничивается при использовании диапазоном 0–0.2; esp.history_seconds — 0.025–0.5. Цвет задаётся объектом Color с RGBA. misc.bhop удалён.
esp.visible_only управляет фильтром обычного ESP: игрок считается видимым, если открыта хотя бы одна проверенная точка тела. Проверяются кости и крайние точки поверхности деформированной модели из текущей камеры. esp.chams_only_visible независимо включает отсечение скрытых частей chams, overlay, wireframe и glow по карте глубины BVH: видимая рука не открывает подсветку всего тела. Режим также применяется к chams оружия противников и следам chams. Без доступной геометрии или камеры соответствующий режим скрывает эффект. Проверка использует снимок физической геометрии карты; дым, прозрачные материалы и перемещённые динамические объекты не воспроизводят точную видимость движка CS2.
menu.frame_limit принимает значения от 100 до 1000 FPS; по умолчанию установлено 400. Значения вне диапазона приводятся к ближайшей границе. Это ограничение частоты оверлея, а не гарантия фактического FPS.
autofire.key_mode: 0 = Hold, 1 = Toggle. Системы autofire и trigger независимы и имеют отдельные key.
Sound ESP: esp.sound включает затухающие 3D-кольца. esp.sound_enemies_only исключает союзников; esp.sound_label включает подпись с расстоянием. esp.sound_color задаёт RGBA. Параметры: esp.sound_lifetime — 0.3–3 секунды, esp.sound_radius — 8–100 игровых единиц, esp.sound_distance — 1–100 метров, esp.sound_thickness — 0.5–4 пикселя. Событие определяется по изменению читаемого C_CSPlayerPawn::m_flEmitSoundTime. Позиция игрока фиксируется при наблюдении изменения; кольцо затем не следует за игроком. Первое значение задаёт базу и не создаёт метку. Звук не определяется по скорости движения. Это индикатор обновления звуковой метки игрока, а не перехват аудиопотока: API не подтверждает, был ли звук слышен локальному игроку, и не различает типы звуков. Пропущенные между чтениями события и недоступные клиенту источники не восстанавливаются. Фильтр Only visible ESP не скрывает звуковые метки.
input и keys
input.is_down(vk) возвращает состояние Windows virtual-key. input.mouse_position() возвращает экранную позицию курсора как Vec2. input.key_name(vk) возвращает читаемое имя клавиши для VK-кода.
Таблица keys содержит: mouse1, mouse2, mouse3, shift, control, alt, insert, delete, space. Можно передать и числовой VK-код.
if input.is_down(keys.shift) then client.log("Shift удерживается") end
Собственный бинд можно задать через ui.keybind в menu, сохранить результат в переменной скрипта и проверять через input.is_down в tick или render. Такой бинд не меняет встроенную настройку приложения и сбрасывается при reload; для встроенного бинда используйте ui.config_keybind.
ui — элементы меню
Вызывайте UI-функции из events.on("menu", ...). Меню-колбэк рисуется под соответствующим скриптом на вкладке Lua. Вне этой вкладки он не вызывается.
| Вызов | Результат |
|---|---|
ui.text(text) | Обычный текст |
ui.colored_text(text, color) | Цветной текст |
ui.separator(), ui.same_line(), ui.spacing() | Компоновка |
ui.button(label) | true в кадре клика |
ui.tooltip(text) | Подсказка к предыдущему элементу при наведении |
ui.checkbox(label, initial?) | Собственный булевый флаг скрипта |
ui.config_checkbox(label, key) | Чекбокс, напрямую связанный с булевым config-ключом |
ui.slider_int(label, min, max, initial?) | Собственное целое значение |
ui.slider_float(label, min, max, initial?) | Собственное число |
ui.input_int(label, initial?) | Ввод целого значения |
ui.input_float(label, initial?) | Ввод числа |
ui.combo(label, {"A", "B", ...}, initialIndex?) | Выбор варианта, индекс с 1 |
ui.input_text(label, initial?) | Строка до 255 байт |
ui.keybind(label, initial?) | Захват VK-кода |
ui.config_keybind(label, key) | Захват встроенного бинда по config-пути |
ui.color_edit(label, initial?) | Color с alpha |
ui.begin_window(title) / ui.end_window() | Начало и конец изменяемого по размеру окна; даже при false у begin_window нужно вызвать end_window |
ui.window(title, function() ... end) | Удобный вариант окна с callback и автоматическим закрытием; возвращает видимость содержимого |
У собственных контролов состояние привязано к label и сохраняется между кадрами, но не между reload. initial применяется только при первом показе контрола. Для прямой синхронизации булевой настройки используйте ui.config_checkbox; для других типов явно вызывайте config.get/config.set.
Окна ui.begin_window и ui.window можно перемещать и менять в размере. Рисуйте их в menu-событии: оно исполняется, когда открыта вкладка Lua. У ui.begin_window всегда ставьте парный ui.end_window, даже если окно свернуто. Если callback завершится ошибкой, незакрытые окна этого скрипта закрываются автоматически, но на это не стоит полагаться как на обычную компоновку.
events.on("menu", function()
ui.config_checkbox("Aimbot", "aim.enabled")
ui.separator()
local color = ui.color_edit("My color", render.color(100, 200, 255))
if ui.button("Show color") then
client.log(("RGB: %d, %d, %d"):format(color.r, color.g, color.b))
end
end)
client и лог
| Вызов | Результат |
|---|---|
client.api_version | 1 |
client.realtime() | Реальное время игры из последнего игрового кадра; вне матча 0 |
client.frame_time() | Длительность текущего overlay-кадра в секундах |
client.map() | Имя текущей карты, если оно доступно |
client.log(text) | Информационная запись с именем скрипта |
print(...) | Тоже пишет в лог; аргументы разделяются табуляцией |
В облачной Dev-сборке ошибки доступны под скриптом в менеджере. Локальный logs.txt не создаётся. client.log принимает несколько аргументов, объединяя их без разделителя; print разделяет аргументы табуляцией.
gamesensical read-only
Дополнительные таблицы renderer, globals, методы entity.get_*, callbacks client.set_event_callback и ui.new_* описаны в отдельном справочнике. Он содержит точные сигнатуры и отличия от оригинальной API: события кадра оверлея, приблизительные hitbox-позиции, статический BVH и поведение вне матча. Запись свойств, FFI, создание игровых интерфейсов, client.exec и изменение команд игрока отсутствуют.