Implemented in src/lua/luaengine.cpp.
Every other page in this reference documents functions your
Lua script calls into the engine. This page is the reverse
direction: global Lua functions the engine looks up and calls
from C++, if you define them. Every one of these is
optional — the engine checks whether the global exists and is
a function before calling it, and silently no-ops if it isn't
defined (except on_load_stage, see below).
Define these as plain global functions (not table members) —
the engine looks them up by exact name via lua_getglobal.
on_update(deltaMilli)Called once per frame, after the engine's own per-frame bookkeeping (script queue processing, sprite pool update). This is the main per-frame hook — virtually all real-time game logic (movement, input polling, timers you're tracking by hand) lives here.
Only one on_update global can exist — the engine calls exactly
that one function per frame. If your project needs several
independent per-frame systems, building a small fan-out (a table
of registered callbacks, each invoked from your own single
on_update) is a common pattern, but it's entirely your own code
to write — the engine has no opinion on it.
function on_update(dt)
print("frame delta: " .. dt .. "ms")
end
on_move_sprites_to_screen(stateId) -> handledCalled when the ScriptProcessor queue reaches an
sp_move_sprites_to_screen(stateId) command (see scripting.md).
Return true if you handled this stateId (spawned something);
returning false/nothing tells the engine this particular
wave-spawn id wasn't recognized.
function on_move_sprites_to_screen(stateId)
if stateId == STATE_MOVE_SATELLITES_TO_SCREEN_RANDOM then
-- spawn logic here
return true
end
return false
end
on_load_stage(stageId) -> successCalled when the queue reaches an sp_load_stage(stageId)
command. Unlike the other hooks, this one is expected to always
be defined and to always claim a valid stageId — there's no
C++-side fallback if it isn't. Typically loads a map
(tilemap_load_map) and dofiles a stage script.
function on_load_stage(stageId)
if stageId == STAGE_FIRST then
tilemap_load_map(BASE_PATH .. "tilemap/corsair/corsair.tmx", MAP_ALIGNMENT_CENTER_WIDTH_BOTTOM)
dofile(BASE_PATH .. "scripts/stages/1st_stage_corsair.lua")
return true
end
return false
end
get_active_enemy_count() -> countCalled periodically by the engine to resolve
sp_wait_queue_empty() (see scripting.md) — the queue only
unblocks once this returns 0. If you never define this global
at all, the engine treats it as always reporting 0 active
enemies, so sp_wait_queue_empty() unblocks immediately rather
than actually waiting — define this accurately if your stage
scripts rely on sp_wait_queue_empty to gate wave pacing.
function get_active_enemy_count()
local total = 0
for _, list in pairs(activeEnemiesByType) do
total = total + #( list )
end
return total
end
collision_set_handler/collision_set_tile_handler follow
the same "engine calls into a Lua global" shape as everything
on this page, but since they're registered via an explicit
function call rather than just defining a magic global name,
they're documented alongside the rest of the collision API in
collision.md.