276 lines
7.2 KiB
C++
276 lines
7.2 KiB
C++
#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 <cartridge/parser.h>
|
|
#include <wx/dir.h>
|
|
#include <wx/filename.h>
|
|
#include <wx/stdpaths.h>
|
|
|
|
extern "C" {
|
|
#include <lua.h>
|
|
#include <lauxlib.h>
|
|
#include <lualib.h>
|
|
}
|
|
|
|
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<const char *>(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();
|
|
}
|
|
|