Lua Api

There is a basic lua api you can use to control cheat behavior from lua

Color representation

Colors are represented the same way as they are in the cheat internally. A color is an unsigned 32 bit integer, the format of the color is 0xAABBGGRR (example: 0xff0000ff is red)

oink.in_screenshot() - Checks if a screenshot is being taken

oink.config_get(key) - Gets a config value

oink.config_set(key, value) - Sets a config value

oink.log(color, text) - Logs text to top left of the screen

oink.event_listen(event, handler_name, handler) - Adds an event handler

oink.event_remove(event, handler_name) - Removes an event handler

oink.lua_to_c_function(func) - Converts a lua function to a C lua function

oink.username() - Returns the current username

oink.dump_bytecode(function[, strip]) - Gets a functions bytecode

oink.load_bytecode(bytecode) - Loads a function from bytecode

oink.get_original(name) - Gets the original copy of any given lua c function

oink.change_in_game_name(name) - Changes your in game name.

oink.ui_context(context) - Creates and sets your UI context.

oink.ui_slider(name, min, max, increment[, default]) - Creates a slider.

oink.ui_button(name) - Creates a button.

oink.ui_checkbox(name[, default]) - Creates a checkbox.

oink.ui_text(name) - Creates a text edit.

oink.ui_get(context, name) - Gets the value of a created UI element.

oink.view_pos() - Returns the players view position.

oink.view_angles() - Returns the players view angles.

oink.view_forward() - Returns the players normalized forward direction.

oink.mark_player(steamid, relation) - Marks a player as a friend/enemy

oink.get_player_relationship(steamid) - Get a player's relationship status

oink.is_spectated() - Checks if you are being spectated using the cheats spectator list.

Events

Below is a list of events to be used with oink.event_listen and oink.event_remove

Example Scripts

Screengrab proof rendering

local function screenshot_proof_visuals()
    -- draw something
    surface.SetDrawColor(0, 0, 0, 128)
    surface.DrawRect(50, 50, 128, 128)
end

oink.event_remove("view_render_post", "Api Example")
oink.event_listen("view_render_post", "Api Example", function()
    -- dont render if we are in a screenshot
    if oink.in_screenshot() then
        return
    end

    -- setup a render context and draw
    cam.Start2D()
    screenshot_proof_visuals()
    cam.End2D()
end)

Log whenever nospread is turned on

-- gets if nospread is on
local function is_nospread_on()
    return oink.config_get("misc.no_spread")
end

-- get the initial state of nospread
local state = is_nospread_on()

-- execute every frame
hook.Remove("Think", "Api Example")
hook.Add("Think", "Api Example", function()
    -- gets current state of nospread
    local new_state = is_nospread_on()

    -- if it was off, and now its on then log
    if not state and new_state then
        local enemy_color = oink.config_get("esp.enemy_override_color")

        oink.log(0xff0000ff, "[api example] red alert!")
        oink.log(0xff00ff00, "[api example] green alert!")
        oink.log(0xffff0000, "[api example] blue alert!")
        oink.log(enemy_color, "[api example] same as enemy color!")
    end

    -- update the state
    state = new_state
end)

Syncs player esp colors

local function sync_settings(settings)
    local value = nil

    for _, setting in pairs(settings) do
        if value == nil then
            value = oink.config_get(setting)
        end

        oink.config_set(setting, value)
    end
end

sync_settings({
    "esp.players.player.box.visible",
    "esp.players.player.name.visible",
    "esp.players.player.weapon.visible",
    "esp.players.player.usergroup.visible",
    "esp.players.player.distance.visible",
    "esp.players.player.team.visible",
})

sync_settings({
    "esp.players.player.box.hidden",
    "esp.players.player.name.hidden",
    "esp.players.player.weapon.hidden",
    "esp.players.player.usergroup.hidden",
    "esp.players.player.distance.hidden",
    "esp.players.player.team.hidden",
})

Pre autorun c function detouring

-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN
-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN
-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN
-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN
-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN
-- NOTE: THIS SCRIPT MUST BE RUN IN PRE STARTUP AUTORUN

-- WHY THIS WORKS: by running in pre autorun, we have access to the globals before anything else does so we can modify
-- the table without anyone being able to fetch an original copy, then by masking our hooked function as a lua c function
-- using the oink api, it makes it impossible to tell that its not a function that gmod declared leading to a very good detour

local _file_Time = file.Time

file.Time = oink.lua_to_c_function(function(path, game_path)
    if game_path == "oinking" then
        return 123456
    end

    return _file_Time(path, game_path)
end)

print("lua c detour demo!")
print("original: " .. tostring(_file_Time))
print("new: " .. tostring(file.Time))

Bytecode loading and unloading

-- saves the function below to bytecode
local bytecode = oink.dump_bytecode(function()
    print("hello, this function is dumped and saved from bytecode")
end)

-- prints the bytecode
print("example bytecode: " .. tostring(bytecode))

-- loads the bytecode
local loaded = oink.load_bytecode(bytecode)
loaded()

Calling original versions of functions to avoid detours

local _LocalPlayer = oink.get_original("_G.LocalPlayer")
local _Player_SteamID64 = oink.get_original("_R.Player.SteamID64")
local _chat_AddText = oink.get_original("_G.chat.AddText")

local lp = _LocalPlayer()
local sid = _Player_SteamID64(lp)
_chat_AddText("your steamid64 is " .. tostring(sid)) 

Creating and using UI elements from lua

-- first create a context for our ui elements
oink.ui_context("My Application")

-- then create our UI elements
oink.ui_button("Button")
oink.ui_slider("Slider A", 0, 1000, 1)
oink.ui_slider("Slider B", -10, 10, 0.05)

-- some number
local number = 100

-- handle button presses
oink.event_remove("view_render_post", "oink_dupe_stealer")
oink.event_listen("view_render_post", "oink_dupe_stealer", function()
    -- NOTE: this can be done in any hook, but its best if you do it in ones that are called every frame
    if oink.ui_get("My Application", "Button") then
        number = number + 1

        local a = oink.ui_get("My Application", "Slider A")
        local b = oink.ui_get("My Application", "Slider B")

        oink.log(0xff0000ff, "[api example] " .. tostring(number) .. ": the values of the sliders are " .. tostring(a) .. " and " .. tostring(b))
    end
end)