Skip to content

Quick Example

This example shows a small CONTROL module with explicit setting defaults, assigned-player access, and a command issued from Update().

Module Structure

modules/
└── my_first_module/
    ├── my_first_module.main.lua
    └── utils/
        └── logger.lua

Full Example

local logger = require("utils.logger")
local announced = false

function Load(playerId)
    Settings.AddBool("Highlight Villagers", true)
    Settings.AddColor("Villager Color", Color(0, 255, 0, 90))
    Settings.AddKeybind("Fast Speed Key", Key.Add)
    Settings.AddTooltip("Fast Speed Key", "Hold to temporarily increase game speed.")

    logger.info("Load complete for player " .. tostring(playerId))
end

function Init()
    announced = false
    logger.info("Match initialized for player " .. tostring(GetAssignedPlayerId()))
end

function Update()
    if not announced then
        ChatMessage("Module active for player " .. tostring(GetAssignedPlayerId()))
        announced = true
    end

    local fastKey = Settings.GetKeybind("Fast Speed Key", Key.Add)
    if IsKeyPressed(fastKey) then
        SetGameSpeedMultiplier(3.0)
    else
        SetGameSpeedMultiplier(1.0)
    end
end

function Render()
    if not Settings.GetBool("Highlight Villagers", true) then
        return
    end

    local color = Settings.GetColor("Villager Color", Color(0, 255, 0, 90))
    local player = GetAssignedPlayer()
    if not player then
        return
    end

    for _, villager in ipairs(player:GetObjectsByClass(UnitClass.VILLAGER)) do
        if villager:IsAlive() then
            RenderObjectBoundsFilled(villager, color)
        end
    end
end

function End(hasWon)
    logger.info(hasWon and "Assigned player won." or "Assigned player lost.")
end

Logger Submodule

local logger = {}

function logger.info(message)
    Log("[my_first_module] " .. message)
end

return logger

What This Example Covers

API Why it is here
require("utils.logger") Loads a local helper module.
Load(playerId) Receives the assigned player id before the match starts.
Settings.Add* Declares module UI settings during Load(playerId).
Settings.Get* Reads setting values with explicit fallback arguments.
GetAssignedPlayer() Uses the player assigned to this module instance.
GetAssignedPlayerId() Reads the controlled player id directly, including in Load.
ChatMessage() Shows a game command used from Update().
RenderObjectBoundsFilled() Draws an overlay on owned villagers.

Map API Add-On

function Render()
    local player = GetAssignedPlayer()
    if not player then
        return
    end

    local selected = player:GetSelectedObject()
    if not selected or not selected:IsVisible() then
        return
    end

    local tile = GetMapTile(Vector2(selected:GetPosition().x, selected:GetPosition().y))
    if not tile then
        return
    end

    local tilePos = tile:GetPos()
    local text = "Tile " .. tostring(tilePos.x) .. "," .. tostring(tilePos.y)
        .. " terrain=" .. tostring(tile:GetTerrain())
        .. " moving=" .. tostring(selected:IsMoving())
    RenderWorldText(text, selected:GetPosition(), 24.0, Color(0, 255, 0), true, true)
end

This add-on uses the GetMapTile(Vector2) overload and stays safe in fog-aware mode by checking selected:IsVisible() before reading other object methods.

Folder Convention

Use modules/{moduleName}/{moduleName}.main.lua or .main.module. CONTROL discovers entries recursively up to depth 3.