|
Scarab
A thin, game-agnostic 2D engine host built on the sunlight tile-map engine
|
Implements a C++ abstraction for using Lua engine;. More...
#include <luaengine.h>
Public Member Functions | |
| LuaEngine (SunLight ::Scripting ::ScriptProcessor *pScriptProcessor) | |
| Constructor. Initialize all class data;. | |
| virtual | ~LuaEngine (void) |
| Destructor. Finalize all class data;. | |
| bool | RunFile (std ::string strFileName) |
| Execute a lua script - this is the ONE entry point that loads the game's very first script (EngineHost::LoadLuaScript), before Lua's own overridden "dofile" global (LuaFileSystemApi::DoFile) even exists to be called. Originally used the C API's luaL_dofile(), which reads via a raw fopen() internally - a real gap found while building Phase 12's archive-distribution support: every other resource load in this engine (dofile, load_json, textures, sound, tilemaps) already routed through SunLight::FileSystem, but this one didn't, so the first script of an archive-distributed game could never even be reached. Reads the file itself via SunLight::FileSystem::FileSystemFactory instead (mirrors LuaFileSystemApi::DoFile's own approach exactly - luaL_loadbuffer + lua_pcall in place of luaL_loadfile's internal fopen), so this now works identically whether strFileName is a real loose-directory path or a virtual path inside a mounted archive. | |
| void | CallOnUpdate (uint32_t nDeltaMilli) |
| Call the optional Lua on_update(dt) global once per frame, if defined. Absent global is a silent no-op so stages that don't define it keep working. | |
| bool | TryDispatchMoveSpritesToScreen (uint16_t nStateId) |
| Dispatch a MOVE_SPRITES_TO_SCREEN_CMD state id to Lua, via the optional global on_move_sprites_to_screen(stateId). The state id itself is entirely Lua-owned (see src/wavestates.lua) - C++ just ferries the ScriptProcessor's queued integer back to Lua opaquely, so stage scripts' sp_move_sprites_to_screen(...) calls (and their sp_wait/sp_wait_queue_empty pacing) work the same regardless of which enemy type a given id belongs to. | |
| bool | TryDispatchLoadStage (uint16_t nStageId) |
| Give Lua first refusal on a LOAD_STAGE_CMD state id, via the optional Lua global on_load_stage(stageId) (bootstrap.lua) - the same "Lua gets first refusal" shape as TryDispatchMoveSpritesToScreen. Since stage bootstrap is a single mechanism (not per-type like the enemy ports), there is no legacy C++ fallback here - Lua is expected to always claim a valid stage id. | |
| int | GetActiveEnemyCount (void) |
| Ask Lua how many enemies it currently has active, via the optional global get_active_enemy_count(). EngineHost::CheckSpritesQueueEmpty's "is the screen clear, unblock sp_wait_queue_empty()" check is entirely driven by this - every enemy type is Lua/SpritePool-owned, so this count is the only source of truth for whether the screen is clear. | |
| const std::string & | GetApplicationDirectory (void) const |
| Return the running executable's own directory (also exposed to Lua as APP_DIR by Init above) - used by EngineHost to locate the bootstrap entry script on disk before any Lua exists to do so itself. | |
| void | Init (Engine ::IRendererProvider *pRendererProvider, SunLight ::Sound ::SoundManager *pSoundManager, Engine ::SpritePool *pSpritePool) |
| Give Lua access to the renderer provider/SoundManager/SpritePool and register the primitive API modules (camera, input, tilemap, sound, sprite) that depend on them. Called from EngineHost's constructor body (not it's member-init list) since it needs m_LuaEngine to already exist as a constructed object to call a method on it - the provider itself is just the constructor's own parameter, forwarded straight through (EngineHost keeps no ITileMap* member of it's own; it has no remaining C++-side use for one). Also resolves and exposes the running executable's own directory as the Lua global APP_DIR - deciding where resources actually live relative to that (BASE_PATH) is main.lua's own job, not C++'s; the one exception is the bootstrap entry script itself, which EngineHost must locate on disk before any Lua exists to make that decision (see GetApplicationDirectory below). | |
| void | AttachToTileMap (SunLight ::TileMap ::ITileMap &tileMap) |
| Attach the collision listener to a renderer's collision manager. Called by the renderer provider's created-hook, once per renderer - the renderer no longer exists yet when Init() runs, since a script may create it (renderer_create) or leave it to the default. | |
| void | InstallVirtualClock (void) |
| Headless (–headless) only - make Lua's own os.time()/ os.clock() follow the null renderer's virtual clock instead of real time (see LuaAppApi::InstallVirtualClock for the why and the set_timer-callback caveat). Must run after Init(), since the replacements read the draw surface Init() stashes. | |
Implements a C++ abstraction for using Lua engine;.
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.
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.
| Scarab::Lua::LuaEngine::LuaEngine | ( | SunLight ::Scripting ::ScriptProcessor * | pScriptProcessor | ) |
Constructor. Initialize all class data;.
| pScriptProcessor | Pointer to an initialized script processor; |
|
virtual |
Destructor. Finalize all class data;.
| void Scarab::Lua::LuaEngine::AttachToTileMap | ( | SunLight ::TileMap ::ITileMap & | tileMap | ) |
Attach the collision listener to a renderer's collision manager. Called by the renderer provider's created-hook, once per renderer - the renderer no longer exists yet when Init() runs, since a script may create it (renderer_create) or leave it to the default.
| tileMap | The just-created renderer's tile map; |
| void Scarab::Lua::LuaEngine::CallOnUpdate | ( | uint32_t | nDeltaMilli | ) |
Call the optional Lua on_update(dt) global once per frame, if defined. Absent global is a silent no-op so stages that don't define it keep working.
| nDeltaMilli | Milliseconds elapsed since the last frame; |
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
| int Scarab::Lua::LuaEngine::GetActiveEnemyCount | ( | void | ) |
Ask Lua how many enemies it currently has active, via the optional global get_active_enemy_count(). EngineHost::CheckSpritesQueueEmpty's "is the screen clear, unblock sp_wait_queue_empty()" check is entirely driven by this - every enemy type is Lua/SpritePool-owned, so this count is the only source of truth for whether the screen is clear.
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
| const std::string & Scarab::Lua::LuaEngine::GetApplicationDirectory | ( | void | ) | const |
Return the running executable's own directory (also exposed to Lua as APP_DIR by Init above) - used by EngineHost to locate the bootstrap entry script on disk before any Lua exists to do so itself.
| void Scarab::Lua::LuaEngine::Init | ( | Engine ::IRendererProvider * | pRendererProvider, |
| SunLight ::Sound ::SoundManager * | pSoundManager, | ||
| Engine ::SpritePool * | pSpritePool | ||
| ) |
Give Lua access to the renderer provider/SoundManager/SpritePool and register the primitive API modules (camera, input, tilemap, sound, sprite) that depend on them. Called from EngineHost's constructor body (not it's member-init list) since it needs m_LuaEngine to already exist as a constructed object to call a method on it - the provider itself is just the constructor's own parameter, forwarded straight through (EngineHost keeps no ITileMap* member of it's own; it has no remaining C++-side use for one). Also resolves and exposes the running executable's own directory as the Lua global APP_DIR - deciding where resources actually live relative to that (BASE_PATH) is main.lua's own job, not C++'s; the one exception is the bootstrap entry script itself, which EngineHost must locate on disk before any Lua exists to make that decision (see GetApplicationDirectory below).
| pRendererProvider | Pointer to the renderer provider every renderer-needing primitive reaches the tile map/draw surface through (the renderer itself doesn't exist yet - see IRendererProvider); |
| pSoundManager | Pointer to the engine's sound manager instance; |
| pSpritePool | Pointer to the engine's sprite pool instance; |
| void Scarab::Lua::LuaEngine::InstallVirtualClock | ( | void | ) |
| bool Scarab::Lua::LuaEngine::RunFile | ( | std ::string | strFileName | ) |
Execute a lua script - this is the ONE entry point that loads the game's very first script (EngineHost::LoadLuaScript), before Lua's own overridden "dofile" global (LuaFileSystemApi::DoFile) even exists to be called. Originally used the C API's luaL_dofile(), which reads via a raw fopen() internally - a real gap found while building Phase 12's archive-distribution support: every other resource load in this engine (dofile, load_json, textures, sound, tilemaps) already routed through SunLight::FileSystem, but this one didn't, so the first script of an archive-distributed game could never even be reached. Reads the file itself via SunLight::FileSystem::FileSystemFactory instead (mirrors LuaFileSystemApi::DoFile's own approach exactly - luaL_loadbuffer + lua_pcall in place of luaL_loadfile's internal fopen), so this now works identically whether strFileName is a real loose-directory path or a virtual path inside a mounted archive.
| strFileName | Lua script file name to execute; |
| bool Scarab::Lua::LuaEngine::TryDispatchLoadStage | ( | uint16_t | nStageId | ) |
Give Lua first refusal on a LOAD_STAGE_CMD state id, via the optional Lua global on_load_stage(stageId) (bootstrap.lua) - the same "Lua gets first refusal" shape as TryDispatchMoveSpritesToScreen. Since stage bootstrap is a single mechanism (not per-type like the enemy ports), there is no legacy C++ fallback here - Lua is expected to always claim a valid stage id.
| nStageId | The stage id (src/stageids.lua) received from the queue; |
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
| bool Scarab::Lua::LuaEngine::TryDispatchMoveSpritesToScreen | ( | uint16_t | nStateId | ) |
Dispatch a MOVE_SPRITES_TO_SCREEN_CMD state id to Lua, via the optional global on_move_sprites_to_screen(stateId). The state id itself is entirely Lua-owned (see src/wavestates.lua) - C++ just ferries the ScriptProcessor's queued integer back to Lua opaquely, so stage scripts' sp_move_sprites_to_screen(...) calls (and their sp_wait/sp_wait_queue_empty pacing) work the same regardless of which enemy type a given id belongs to.
| nStateId | The wave-spawn state id received from the queue; |
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