#include "app.h" #include "lua/game_engine.h" #include "lua/media_manager.h" #include "lua/persistence.h" #include "lua/wherigo.h" #include "lua/wherigo_completion.h" #include "ui/start_screen.h" #include #include #include #include extern "C" { #include #include #include } bool cApp::OnInit() { // Initialize image handlers for JPEG, PNG, GIF, etc. wxInitAllImageHandlers(); // Show start frame auto *startFrame = new cStartScreen(); startFrame->Show(true); return true; } bool cApp::loadCartridge(const std::string &filePath) { // Unload previous cartridge if any unloadCartridge(); m_cartridgePath = filePath; // Parse cartridge m_cartridge = cartridge::parseFile(filePath); if (!m_cartridge) { wxLogError("Failed to parse cartridge: %s", filePath); return false; } return initLuaState(); } bool cApp::initLuaState() { auto luac = m_cartridge->luac(); if (!luac) { wxLogError("No Lua bytecode in cartridge"); return false; } m_luaState = luaL_newstate(); if (!m_luaState) { wxLogError("Failed to create Lua state"); return false; } luaL_openlibs(m_luaState); // Reset media counter before loading cartridge wherigo::resetMediaCounter(); // Register Wherigo module wherigo::luaopen_Wherigo(m_luaState); lua_pop(m_luaState, 1); const auto &bytecode = luac->getData(); int result = luaL_loadbuffer(m_luaState, reinterpret_cast(bytecode.data()), bytecode.size(), "cartridge"); if (result != 0) { const char *err = lua_tostring(m_luaState, -1); wxLogError("Lua load error: %s", err); lua_close(m_luaState); m_luaState = nullptr; return false; } // Execute the loaded chunk if (lua_pcall(m_luaState, 0, 0, 0) != 0) { const char *err = lua_tostring(m_luaState, -1); wxLogError("Lua exec error: %s", err); lua_close(m_luaState); m_luaState = nullptr; return false; } // Find the cartridge object by searching for ClassName = "ZCartridge" m_cartridgeRef = LUA_NOREF; lua_getglobal(m_luaState, "_G"); lua_pushnil(m_luaState); while (lua_next(m_luaState, -2) != 0) { if (lua_istable(m_luaState, -1)) { lua_getfield(m_luaState, -1, "ClassName"); if (lua_isstring(m_luaState, -1)) { const char *className = lua_tostring(m_luaState, -1); if (strcmp(className, "ZCartridge") == 0) { lua_pop(m_luaState, 1); // pop ClassName m_cartridgeRef = luaL_ref(m_luaState, LUA_REGISTRYINDEX); // store reference, pops table lua_pop(m_luaState, 1); // pop key break; } } lua_pop(m_luaState, 1); // pop ClassName } lua_pop(m_luaState, 1); // pop value } lua_pop(m_luaState, 1); // pop _G // Initialize media manager wherigo::MediaManager::getInstance().init(m_luaState, m_cartridge.get()); if (m_cartridgeRef == LUA_NOREF) { wxLogError("No ZCartridge object found!"); return false; } // Initialize the game engine (but don't start yet) wherigo::GameEngine::getInstance().init(m_luaState, m_cartridgeRef); return true; } void cApp::startGame() { if (!m_luaState || m_cartridgeRef == LUA_NOREF) { wxLogError("No cartridge loaded"); return; } // Call OnStart callback lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_cartridgeRef); lua_getfield(m_luaState, -1, "OnStart"); if (lua_isfunction(m_luaState, -1)) { lua_pushvalue(m_luaState, -2); // push cartridge as self if (lua_pcall(m_luaState, 1, 0, 0) != 0) { const char *err = lua_tostring(m_luaState, -1); wxLogError("OnStart error: %s", err); lua_pop(m_luaState, 1); } } else { lua_pop(m_luaState, 1); } lua_pop(m_luaState, 1); // pop cartridge // Start the game engine wherigo::GameEngine::getInstance().start(); } void cApp::unloadCartridge() { // Shutdown the game engine wherigo::GameEngine::getInstance().shutdown(); if (m_cartridgeRef != LUA_NOREF && m_luaState) { luaL_unref(m_luaState, LUA_REGISTRYINDEX, m_cartridgeRef); m_cartridgeRef = LUA_NOREF; } if (m_luaState) { lua_close(m_luaState); m_luaState = nullptr; } m_cartridge.reset(); m_cartridgePath.clear(); } std::string cApp::getAutoSavePath() const { if (m_cartridge) { wxStandardPaths& stdPaths = wxStandardPaths::Get(); wxString userDataDir = stdPaths.GetUserDataDir(); // Create save directory if it doesn't exist if (!wxDir::Exists(userDataDir)) { wxFileName::Mkdir(userDataDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); } // Generate save filename from cartridge name wxString cartName = wxString::FromUTF8(m_cartridge->cartridgeName()); cartName.Replace(" ", "_"); cartName.Replace("/", "_"); wxString savePath = userDataDir + wxFileName::GetPathSeparator() + cartName + ".save"; return savePath.ToStdString(); } return ""; } bool cApp::saveGameState(const std::string &saveFilePath) { if (!m_luaState) { wxLogError("No Lua state to save"); return false; } std::string savePath = saveFilePath.empty() ? getAutoSavePath() : saveFilePath; if (savePath.empty()) { wxLogError("No save path available"); return false; } bool success = wherigo::LuaPersistence::saveState(m_luaState, savePath); if (success) { wxLogMessage("Game saved to: %s", savePath); } return success; } bool cApp::loadGameState(const std::string &saveFilePath) { if (!m_luaState) { wxLogError("No Lua state to load into"); return false; } std::string loadPath = saveFilePath.empty() ? getAutoSavePath() : saveFilePath; if (loadPath.empty() || !wxFileExists(loadPath)) { wxLogError("Save file not found: %s", loadPath); return false; } bool success = wherigo::LuaPersistence::loadState(m_luaState, loadPath); if (success) { wxLogMessage("Game loaded from: %s", loadPath); // Notify game engine of state change wherigo::GameEngine::getInstance().notifyStateChanged(); } return success; } std::string cApp::getCompletionLogPath() const { if (m_cartridge && !m_cartridgePath.empty()) { // Use the same directory as the GWC file wxFileName gwcFile(m_cartridgePath); wxString directory = gwcFile.GetPath(); // Generate completion log filename from cartridge name wxString cartName = wxString::FromUTF8(m_cartridge->cartridgeName()); cartName.Replace(" ", "_"); cartName.Replace("/", "_"); wxString logPath = directory + wxFileName::GetPathSeparator() + cartName + ".gwl"; return logPath.ToStdString(); } return ""; } bool cApp::generateCompletionLog(const std::string &logFilePath) { if (!m_luaState) { wxLogError("No Lua state for completion log"); return false; } std::string logPath = logFilePath.empty() ? getCompletionLogPath() : logFilePath; if (logPath.empty()) { wxLogError("No completion log path available"); return false; } bool success = wherigo::WherigoCompletion::generateCompletionLog(m_luaState, logPath, "Player"); if (success) { wxLogMessage("Completion log generated: %s", logPath); } return success; } int cApp::OnExit() { unloadCartridge(); return wxApp::OnExit(); }