Skip to content

Write reliable scripts

Game state can change between callbacks. A result that was valid one moment can be unavailable on the next update. Use small checks and bounded work.

Save IDs, not live objects

Store an object ID when you must remember an object. Query the object again before you use it.

local watched_id = nil

function Update()
    local object = watched_id and GetObjectById(watched_id) or nil
    if object == nil or object:IsAlive() ~= true then
        watched_id = nil
        return
    end

    -- Use object only in this callback.
end

Do not keep Object or Player values as long-lived state.

Treat unavailable data as normal

Check a lookup before you call a method on its result.

local player = GetAssignedPlayer()
if player == nil then
    return
end

local tile = GetMapTile(10, 10)
if tile == nil then
    return
end

Use ipairs for arrays from the API. An empty array is a normal result.

Limit work in each update

Use a timer for repeated scans. Limit the number of objects that you inspect or command in one update.

local elapsed = 0

function Update(delta_seconds)
    elapsed = elapsed + (tonumber(delta_seconds) or 0)
    if elapsed < 0.25 then
        return
    end
    elapsed = 0

    local player = GetAssignedPlayer()
    if player == nil then
        return
    end

    local objects = player:GetPlayerObjects()
    for index = 1, math.min(#objects, 32) do
        local object = objects[index]
        -- Make one bounded decision.
    end
end

Do not call GetAllMapTiles() in every update. Use GetMapTile when you need a small area.

Draw only in Render

Build a label or color in Update. Draw it in Render. Keep the shared value simple.

local message = "Waiting for a match"

function Update()
    local player = GetAssignedPlayer()
    message = player and ("Player " .. player:GetId()) or "Waiting for a match"
end

function Render()
    RenderTopRightText(message, 12, 12, 14, Color("#FFFFFFFF"), true)
end

Do not depend on screen size or screen position during Update.

Check commands later

A command result of true means that WololoLabs submitted the request. It does not prove that the game accepted or completed it.

if UnitsMove({ unit }, destination) == true then
    -- Read fresh state in a later Update.
end

Record an object ID and a cooldown after a command. Do not submit the same order every update.

Keep helpers simple

Use require for pure Lua helpers. Return one table from each helper. Keep callbacks and API calls in the entry script when you can.

Write useful logs

Use LogInfo, LogWarn, and LogError with one script prefix.

LogWarn("[sample-status] assigned player is unavailable")

Log state changes and failures. Do not log a normal update loop every frame.

Use advanced names carefully

The Advanced section contains compatibility and diagnostic APIs. Isolate each advanced call in a small function. Check its result after you update WololoLabs or the game.