Scooby Docs

GTA 5 / LUA

Lua interfaces and themes

Both ListUI and ClickUI consume native feature pages registered by Lua. features.add_page builds each row from the existing command and each frontend uses its own controls, active style, hotkeys and access checks. Find the page in Lua Content (GTA5 ClickUI: Settings > Lua Content; RDR2 ClickUI: Lua > Lua Content). Reopen Lua Content after adding a page if that view was already open.

local page = assert(features.add_page('My Self Tools', {'godmode'}))
-- Later: features.remove_page(page)

Pages belong to their Lua state. Reload/stop/unload expires them, even if an old native-page snapshot is open. Scripts can remove only their own pages. Each script can register up to 64 pages with up to 256 IDs per page. IDs are exact, case-sensitive command names, obtained from features.list() independently in each game. Permission/capability settings are excluded from this API.

Shared feature state in a standalone interface

features.list(filter) and features.get(id) return snapshots with id, label, description, type, value and available. Numeric bounds are min/max when defined. List entries provide options = {{value=10,label='Example'}, ...}; write the option's actual value, not its array index. Types are boolean, integer, number, list, string, color, vector3 and action.

features.set(id, value) uses the real command setter and returns its resulting snapshot. features.call(id) calls an action and returns true. Invalid input, unknown IDs, unavailable access or calls made during rendering return nil, error message. Bounds are rejected rather than silently changing the requested value. Colors use {x=r,y=g,z=b,w=a} in 0..1; vectors use {x,y,z}. Strings are limited to 65,536 bytes. Existing command callbacks and settings remain in charge of applying and saving changes. An action result means it was dispatched; engine work and asynchronous feature callbacks can finish later.

Read/update commands in game callbacks. Keep ordinary Lua snapshots for rendering and queue writes with script.run_in_fiber. The host serializes access to a script state. Refreshing a snapshot also picks up changes made in the main UI or by hotkeys; do not repeatedly force an old local boolean onto the host.

local I = ImGuiCpp
local write = type(log) == 'function' and log or log.info
local god = I.Ref(false)
script.register_looped('sync-godmode', function(task)
    local feature = features.get('godmode')
    if feature then god.value = feature.value end
    task:sleep(100)
end)
script.register_render(function()
    if I.Begin('My Self UI') then
        if I.Checkbox('God Mode', god) then
            local desired = god.value
            script.run_in_fiber(function()
                local result, err = features.set('godmode', desired)
                if not result then write(err) end
            end)
        end
    end
    I.End()
end)

The complete SharedFeatureUI.lua example adds search, native command controls, custom window styling, a theme editor and F7 visibility. NativeFeaturePage.lua shows the native frontend extension. They are ordinary scripts, unlike the three generated developer reference files.

GTA5 keeps these examples under examples/lua/; RDR2 keeps them under scripts/examples/. Copy the chosen .lua example into that game's Lua scripts folder, refresh the script list and enable it. The custom window uses F7 to show/hide itself. The native-page example needs no separate window or mouse override.

Theme both native frontends

ui_theme.set_accent(r,g,b,a=255) applies the native accent to ClickUI and ListUI. ui_theme.get() captures {clickui=...,listui=...}. ui_theme.apply(snapshot) restores captured surfaces through their existing settings serializers. Omit a surface to leave it alone. Run these calls from game callbacks, using script.run_in_fiber for buttons in a custom window.

script.run_in_fiber(function()
    local original = ui_theme.get()
    ui_theme.set_accent(80, 160, 240, 255)
    -- Keep original in this script, then restore on an explicit user action:
    -- ui_theme.apply(original)
end)

The raw snapshot schema belongs to the current game and native style. RDR2's listui value is a complete active-style profile, including version and profileStyle; preserve those fields and its validated layout. GTA5 additionally has the existing ui_theme.patch, set, image_folder and reload_images helpers. Discover/edit captured fields instead of copying raw settings between games or renderer styles. Native themes remain saved by the host after a script stops; only script-owned pages and render callbacks are automatically removed.

Standalone windows use ImGuiCpp.PushStyleColor/PopStyleColor and PushStyleVar/PopStyleVar around their own drawing, with balanced stacks. They can have wholly different navigation, windows, tabs, tables, drawing, colors and controls while sharing host feature state. These scoped styles do not replace the native theme. Reuse loaded fonts through GetFonts/PushFont/PopFont.

Porting an existing C++ interface

Start with the C++ ImGui adapter: Refs replace output pointers, tables replace vectors/arrays, and ImGuiCpp retains C++ widget return conventions. Replace native command checkboxes with features.get/set or a native feature page. Replace the C++ renderer callback with script.register_render, and queue engine work onto the script scheduler. The host owns frames, input capture, renderer backends and context lifetime.

Existing C++ interfaces use ImGui style fields and scoped colors; map these to scoped style pushes and custom window rendering. Arbitrary C++ pointers, backend/atlas mutation, docking/multiple OS windows, raw GPU resources and internal draw-list machinery remain outside the portable API. ImGuiCpp.GetCapabilities() lists supported calls and explicit limits. Managed image APIs remain game specific. Game features require that game's command or native implementations; a GTA5 layout does not implement its gameplay in RDR2.

Verification

Run tools/test_lua_host_ui.ps1 -LuaRuntime LuaJIT in each project; RDR2 also supports -LuaRuntime Lua54. The shared production headers are tested for all value types, host-to-Lua changes, invalid input, bounds, account restrictions, render-phase rejection, native row construction, per-script removal and unload. Normal supported Release builds compile both actual adapters and frontend routes. Use the existing Lua API contracts for real ImGui scope/input behavior. In-game appearance, controller interaction and foreground performance require separate testing with the exact DLL; these fixtures do not establish those results.