// ============================================================================ // LUA GAME WRAPPER - IMPLEMENTATION // ============================================================================ // Manages Lua VM lifecycle and script execution #include "lua_game.h" #include "lua_bindings.h" #include #include extern "C" { #include "ff.h" // FatFS for SD card access } LuaGame::LuaGame(const char* script_path, uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager) : Game(width, height, renderer, gui, input_manager), L(nullptr), script_path(script_path), loaded(false) { // Create new Lua state L = luaL_newstate(); if (!L) { error_message = "Failed to create Lua state"; printf("LuaGame: %s\n", error_message.c_str()); return; } // Open standard Lua libraries (math, string, table, coroutine) luaL_openlibs(L); // Register game API bindings lua_bindings_register(L, this); // Load the script loaded = load_script(); if (!loaded) { printf("LuaGame: Failed to load %s: %s\n", script_path, error_message.c_str()); } else { printf("LuaGame: Successfully loaded %s\n", script_path); } } LuaGame::~LuaGame() { if (L) { lua_close(L); L = nullptr; } } bool LuaGame::load_script() { FIL fil; FRESULT fr; // Open Lua script from SD card fr = f_open(&fil, script_path.c_str(), FA_READ); if (fr != FR_OK) { error_message = "Failed to open file (FatFS error: "; error_message += std::to_string((int)fr); error_message += ")"; return false; } // Get file size FSIZE_t file_size = f_size(&fil); if (file_size == 0 || file_size > 64 * 1024) { // Limit to 64KB f_close(&fil); error_message = "Script file size invalid (0 or > 64KB)"; return false; } // Allocate buffer for script char* script_buffer = new char[file_size + 1]; if (!script_buffer) { f_close(&fil); error_message = "Failed to allocate memory for script"; return false; } // Read script into buffer UINT bytes_read; fr = f_read(&fil, script_buffer, file_size, &bytes_read); f_close(&fil); if (fr != FR_OK || bytes_read != file_size) { delete[] script_buffer; error_message = "Failed to read script file"; return false; } script_buffer[file_size] = '\0'; // Load script into Lua int result = luaL_loadbuffer(L, script_buffer, file_size, script_path.c_str()); delete[] script_buffer; if (result != LUA_OK) { report_error("load script"); return false; } // Execute script (loads functions into global namespace) result = lua_pcall(L, 0, 0, 0); if (result != LUA_OK) { report_error("execute script"); return false; } return true; } void LuaGame::init() { if (!loaded) return; // Call Lua init() function if it exists lua_getglobal(L, "init"); if (lua_isfunction(L, -1)) { call_lua_function("init", 0, 0); } else { lua_pop(L, 1); // Pop non-function value printf("LuaGame: Warning - no init() function found\n"); } } bool LuaGame::update(const InputEvent& event) { if (!loaded) return false; // Call Lua update(event) function if it exists lua_getglobal(L, "update"); if (!lua_isfunction(L, -1)) { lua_pop(L, 1); return false; // No update function, no redraw needed } // Push event table to Lua lua_newtable(L); lua_pushstring(L, "type"); lua_pushinteger(L, (int)event.type); lua_settable(L, -3); lua_pushstring(L, "x"); lua_pushinteger(L, event.x); lua_settable(L, -3); lua_pushstring(L, "y"); lua_pushinteger(L, event.y); lua_settable(L, -3); lua_pushstring(L, "button_id"); lua_pushinteger(L, event.button_id); lua_settable(L, -3); lua_pushstring(L, "valid"); lua_pushboolean(L, event.valid); lua_settable(L, -3); // Call update(event) with 1 arg, expecting 1 result (needs_redraw) if (!call_lua_function("update", 1, 1)) { return false; } // Get return value (needs redraw?) bool needs_redraw = lua_toboolean(L, -1); lua_pop(L, 1); return needs_redraw; } void LuaGame::draw() { if (!loaded) return; // Call Lua draw() function if it exists lua_getglobal(L, "draw"); if (lua_isfunction(L, -1)) { call_lua_function("draw", 0, 0); } else { lua_pop(L, 1); } } bool LuaGame::wants_to_exit() const { if (!L) return false; // Check if Lua script requested exit lua_pushstring(L, "__exit_requested"); lua_gettable(L, LUA_REGISTRYINDEX); bool exit = lua_toboolean(L, -1); lua_pop(L, 1); return exit; } bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) { int result = lua_pcall(L, nargs, nresults, 0); if (result != LUA_OK) { report_error(func_name); return false; } return true; } void LuaGame::report_error(const char* context) { const char* msg = lua_tostring(L, -1); if (msg) { error_message = context; error_message += ": "; error_message += msg; printf("LuaGame Error [%s]: %s\n", context, msg); } lua_pop(L, 1); // Pop error message }