Skip to content

Example Scripts

Minimal Script

A simple script that uses ability 2 on the closest target:

lua
name = "Example"
id = hero_id.abrams

function is_enabled()
    return config.get_bool("enabled") and input.is_key_held(VK.XBUTTON2)
end

function on_tick()
    local target = targeting.find_closest_by_fov(5.0, 50.0)
    if not target or not target:is_targetable() then return end

    local lp = local_player()
    if not lp then return end

    if lp:is_ability_ready(slot.ability2) then
        if snap_to_target(target, { bone = bone.spine_1 }) then
            press_ability(slot.ability2)
        end
    end
end

settings = {
    { key = "enabled", type = "bool", default = false, label = "Enabled" },
}

Combo Script

Combos use helpers like snap_to_target, sleep, wait_until_in_range, and press_ability together. on_tick() runs as a coroutine — when it returns, it will be restarted on the next tick.

lua
name = "Bebop"
id = hero_id.bebop

function is_enabled()
    return config.get_bool("enabled")
        and input.is_key_held(config.get_int("activation_key"))
end

function on_tick()
    local target = targeting.find_closest_by_fov(config.get_float("max_fov"), 70.0)
    if not target or not target:is_targetable() then return end

    local lp = local_player()
    if not lp then return end

    -- hook (ability 3)
    local hook = lp:get_ability(slot.ability3)
    if not hook or not hook.learned then return end
    if not lp:is_ability_ready(slot.ability3) then return end

    snap_to_target(target, { bone = bone.spine_1, projectile_velocity = 3100 })
    press_ability(slot.ability3)

    if not wait_for_cooling_down(slot.ability3, 5000) then return end
    if not wait_until_in_range(target, 4.0, 4000) then return end

    -- bomb (ability 2)
    local bomb = lp:get_ability(slot.ability2)
    if config.get_bool("bomb") and bomb and bomb.learned and lp:is_ability_ready(slot.ability2) then
        snap_to_target(target, { bone = bone.spine_1 })
        sleep(100)
        press_ability(slot.ability2)
        sleep(100)
        left_click()

        -- echo shard
        if config.get_bool("echoshard") and lp:has_item("upgrade_ability_power_shard") then
            input.press_key(item_slot_to_key(config.get_int("echoshard_slot")))
            sleep(100)

            if lp:is_ability_ready(slot.ability2) then
                snap_to_target(target, { bone = bone.spine_1 })
                sleep(100)
                press_ability(slot.ability2)
                sleep(100)
                left_click()
            end
        end
    end

    -- uppercut (ability 1)
    local uppercut = lp:get_ability(slot.ability1)
    if config.get_bool("uppercut") and uppercut and uppercut.learned and lp:is_ability_ready(slot.ability1) then
        snap_to_target(target, { bone = bone.spine_1 })
        sleep(100)
        press_ability(slot.ability1)
    end
end

settings = {
    { key = "enabled",        type = "bool",    default = false,       label = "Enabled" },
    { key = "activation_key", type = "keybind", default = VK.XBUTTON2, label = "Activation Key" },
    { key = "max_fov",        type = "float",   default = 5.0,         label = "Max FOV", min = 1.0, max = 15.0 },
    { key = "bomb",           type = "bool",    default = true,        label = "Bomb" },
    { key = "uppercut",       type = "bool",    default = true,        label = "Uppercut" },
    { key = "echoshard",      type = "bool",    default = false,       label = "Echo Shard" },
    { key = "echoshard_slot", type = "item_slot", default = 0,           label = "Echo Shard Slot", depends_on = "echoshard", depends_value = 1 },
}

Key Patterns

Activation with Hold Key

lua
function is_enabled()
    return config.get_bool("enabled")
        and input.is_key_held(config.get_int("hotkey"))
end

Activation Mode (Hold Key vs Always On)

lua
function is_enabled()
    if not config.get_bool("enabled") then return false end
    if config.get_bool("always_on") then return true end
    local key = config.get_int("activation_key")
    return key > 0 and input.is_key_held(key)
end

settings = {
    { key = "enabled",        type = "bool",    default = false,       label = "Enabled" },
    { key = "always_on",      type = "bool",    default = false,       label = "Always On" },
    { key = "activation_key", type = "keybind", default = VK.XBUTTON2, label = "Key", depends_on = "always_on", depends_value = 0 },
}

Multi-Parent Dependencies

Pass a table to depends_on when a setting only makes sense if several parents are all satisfied. Visibility is transitive — turning off any ancestor hides every descendant — and children are auto-indented under their parent.

lua
settings = {
    { key = "dispel_magic",          type = "bool", default = false, label = "Dispel Magic" },
    { key = "include_unstoppable",   type = "bool", default = false, label = "Include unstoppable",
      depends_on = "dispel_magic" },
    { key = "prioritise_unstoppable", type = "bool", default = false, label = "Prioritise Unstoppable",
      depends_on = { "dispel_magic", "include_unstoppable" } },
    { key = "interaction_delay",     type = "int",  default = 250,   label = "Interaction Delay (ms)",
      min = 0, max = 2000, depends_on = "dispel_magic" },
}

Prioritise Unstoppable shows only when both Dispel Magic and Include unstoppable are on. If Dispel Magic is turned off, Include unstoppable becomes hidden, and Prioritise Unstoppable is hidden along with it. depends_value accepts a parallel table (e.g. depends_value = { 1, 1 }) if you need to match a non-default value.

Low-Health Execute

Using find_closest_by_predicate and is_targetable:

lua
function on_tick()
    local lp = local_player()
    if not lp or not lp:is_ability_ready(slot.ability4) then return end

    local target = find_closest_by_predicate(function(e)
        return e:is_targetable() and e:get_health_percentage() < 20
    end, 30.0)

    if not target then return end

    if snap_to_target(target, { bone = bone.spine_1 }) then
        press_ability(slot.ability4)
    end
end

Cooldown Gating with Debounce

Prevent spamming an action with debounce:

lua
function on_tick()
    local target = targeting.find_closest_by_fov(5.0, 30.0)
    if not target or not target:is_targetable() then return end

    -- only fire every 2 seconds
    if debounce("combo", 2000) then
        press_ability(slot.ability1)
    end
end

Custom Mouse Movement

Raw vs smoothed mouse movement:

lua
function on_tick()
    local target = targeting.find_closest_by_fov(5.0, 50.0)
    if not target then return end

    local delta = targeting.compute_aim_delta(target, bone.head)
    if delta:empty() then return end

    -- raw instant flick
    input.move_mouse(delta)

    -- smoothed movement (speed 0.5)
    input.move_mouse(delta, 0.5)

    -- smoothed with detach factor
    input.move_mouse(delta, 0.5, 0.3)
end

Smoothness Compensation

When aiming with smoothing, the crosshair lags behind a moving target. Pass your speed value as the 4th argument to compute_aim_delta — it applies smoothness compensation automatically, predicting where the target will be by the time the smooth cursor catches up.

lua
function on_tick()
    local target = targeting.find_closest_by_fov(5.0, 50.0)
    if not target or not target:is_targetable() then return end

    local speed = config.get_float("smooth_speed")

    -- speed param enables smoothness compensation inside compute_aim_delta
    local delta = targeting.compute_aim_delta(target, bone.head, 0, speed)
    if delta:empty() then return end

    input.move_mouse(delta, speed)
end

The higher-level helpers (aim_at_target, lock_aim) handle this automatically.

Raw Mouse Movement Explained

input.move_mouse(delta) sends a raw mouse delta in pixel units — the game converts it to angle change using the mouse multiplier (0.044) and sensitivity. With no extra args it applies instantly in one frame. With a speed argument (0.01–1.0), it interpolates over multiple frames — lower values are smoother but slower to converge.

lua
function on_tick()
    local target = targeting.find_closest_by_fov(5.0, 50.0)
    if not target then return end

    local delta = targeting.compute_aim_delta(target, bone.head)
    if delta:empty() then return end

    -- raw: full delta applied this frame
    input.move_mouse(delta)

    -- smoothed: only a fraction applied per frame, call every tick
    -- input.move_mouse(delta, 0.3)

    -- smoothed with detach: stops early when close enough
    -- input.move_mouse(delta, 0.3, 0.5)
end

Aim at World Position

Use the vec3 overload of compute_aim_delta to aim at an arbitrary world point instead of a player bone:

lua
function on_tick()
    -- aim at a fixed world coordinate
    local pos = vec3(1200, -500, 300)
    local delta = targeting.compute_aim_delta(pos)
    if delta:empty() then return end

    input.move_mouse(delta, 0.5)
end

Camera and World-to-Screen

Access camera state and project world positions to screen coordinates:

lua
function on_tick()
    local angles = camera.get_view_angles()  -- vec2(pitch, yaw)
    local forward = camera.get_forward()      -- unit vec3
    local fov = camera.get_fov()
    local screen = camera.get_screen_size()   -- vec2(width, height)

    -- project a world position to screen
    local target = targeting.find_closest_by_fov(10.0, 50.0)
    if not target then return end

    local head = target:get_bone_position(bone.head)
    local screen_pos = camera.world_to_screen(head)
    if screen_pos then
        print("head on screen at: " .. tostring(screen_pos))
    end
end

Aim Test Script

Diagnostic script that prints aim state each tick — useful for tuning sensitivity and smoothness:

lua
name = "Aim Test"
id = hero_id.any

function is_enabled()
    return config.get_bool("enabled") and input.is_key_held(VK.XBUTTON2)
end

function on_tick()
    local target = targeting.find_closest_by_fov(10.0, 50.0)
    if not target or not target:is_targetable() then return end

    local fov = targeting.get_fov(target, bone.head)
    local delta = targeting.compute_aim_delta(target, bone.head)
    local angles = camera.get_view_angles()

    print(string.format("fov=%.2f delta=%s angles=%s", fov, tostring(delta), tostring(angles)))

    if fov > 0.5 then
        local speed = config.get_float("speed")
        input.move_mouse(delta, speed)
    end
end

settings = {
    { key = "enabled", type = "bool",  default = false, label = "Enabled" },
    { key = "speed",   type = "float", default = 0.5,   label = "Smooth Speed", min = 0.01, max = 1.0 },
}