// ============================================================================ // LUA GAME WRAPPER - EMULATOR IMPLEMENTATION // ============================================================================ // Manages Lua VM lifecycle and script execution for desktop emulator #include "../games/lua_game.h" #include "../games/lua_bindings.h" #include #include #include #include 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() { // Open Lua script from filesystem (emulator) std::ifstream file(script_path); if (!file.is_open()) { error_message = "Failed to open file: " + script_path; return false; } // Read entire file into string std::stringstream buffer; buffer << file.rdbuf(); std::string script_content = buffer.str(); file.close(); if (script_content.empty()) { error_message = "Script file is empty"; return false; } if (script_content.size() > 64 * 1024) { // Limit to 64KB error_message = "Script file too large (> 64KB)"; return false; } // Load script into Lua int result = luaL_loadbuffer(L, script_content.c_str(), script_content.size(), script_path.c_str()); 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 }