starting playing the wherigo

Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
This commit is contained in:
2026-02-13 02:41:12 +01:00
parent 273e169281
commit 833dd51a84
34 changed files with 4055 additions and 84 deletions

View File

@@ -0,0 +1,228 @@
#include "lua/persistence.h"
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
#include <wx/log.h>
#include <fstream>
#include <sstream>
#include <set>
namespace wherigo {
// List of Wherigo game object classes to save
static const std::set<std::string> WHERIGO_CLASSES = {
"ZCartridge", "Zone", "ZItem", "ZCharacter",
"ZTask", "ZTimer", "ZInput", "ZMedia"
};
std::vector<std::string> LuaPersistence::getGameGlobals(lua_State* L) {
std::vector<std::string> globals;
lua_getglobal(L, "_G");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
if (lua_isstring(L, -2)) {
const char* key = lua_tostring(L, -2);
// Check if it's a Wherigo game object
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "ClassName");
if (lua_isstring(L, -1)) {
std::string className = lua_tostring(L, -1);
if (WHERIGO_CLASSES.count(className) > 0) {
globals.push_back(key);
}
}
lua_pop(L, 1); // pop ClassName
}
// Also save simple global variables (strings, numbers, booleans)
else if (lua_isnumber(L, -1) || lua_isstring(L, -1) || lua_isboolean(L, -1)) {
std::string keyStr = key;
// Skip internal Lua variables
if (keyStr != "_G" && keyStr != "_VERSION" && keyStr.find("_") != 0) {
globals.push_back(key);
}
}
}
lua_pop(L, 1); // pop value
}
lua_pop(L, 1); // pop _G
// Always include Player object
globals.push_back("Player");
return globals;
}
void LuaPersistence::serializeValue(lua_State* L, int index, std::string& output, int depth) {
if (depth > 10) {
output += "nil"; // Prevent infinite recursion
return;
}
int type = lua_type(L, index);
switch (type) {
case LUA_TNIL:
output += "nil";
break;
case LUA_TBOOLEAN:
output += lua_toboolean(L, index) ? "true" : "false";
break;
case LUA_TNUMBER:
output += std::to_string(lua_tonumber(L, index));
break;
case LUA_TSTRING: {
const char* str = lua_tostring(L, index);
output += "\"";
// Escape special characters
for (const char* p = str; *p; ++p) {
if (*p == '"' || *p == '\\') output += '\\';
output += *p;
}
output += "\"";
break;
}
case LUA_TTABLE:
serializeTable(L, index, output, depth);
break;
default:
// Functions, userdata, threads -> save as nil
output += "nil";
break;
}
}
void LuaPersistence::serializeTable(lua_State* L, int index, std::string& output, int depth) {
output += "{";
// Normalize stack index
if (index < 0) index = lua_gettop(L) + index + 1;
bool first = true;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (!first) output += ",";
first = false;
// Serialize key
output += "[";
serializeValue(L, -2, output, depth + 1);
output += "]=";
// Serialize value
serializeValue(L, -1, output, depth + 1);
lua_pop(L, 1); // pop value, keep key for next iteration
}
output += "}";
}
bool LuaPersistence::saveGlobals(lua_State* L, const std::string& filePath,
const std::vector<std::string>& globals) {
std::string output = "-- Wherigo Save State\nreturn {\n";
for (const auto& globalName : globals) {
lua_getglobal(L, globalName.c_str());
if (!lua_isnil(L, -1)) {
output += " [\"" + globalName + "\"] = ";
serializeValue(L, -1, output, 0);
output += ",\n";
}
lua_pop(L, 1);
}
output += "}\n";
// Write to file
std::ofstream file(filePath);
if (!file.is_open()) {
wxLogError("Failed to open save file: %s", filePath);
return false;
}
file << output;
file.close();
wxLogDebug("Saved %zu globals to %s", globals.size(), filePath);
return true;
}
bool LuaPersistence::saveState(lua_State* L, const std::string& filePath) {
auto globals = getGameGlobals(L);
return saveGlobals(L, filePath, globals);
}
bool LuaPersistence::loadGlobals(lua_State* L, const std::string& filePath) {
// Load the file
std::ifstream file(filePath);
if (!file.is_open()) {
wxLogError("Failed to open load file: %s", filePath);
return false;
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
// Execute the Lua file
if (luaL_loadstring(L, content.c_str()) != 0) {
wxLogError("Failed to parse save file: %s", lua_tostring(L, -1));
lua_pop(L, 1);
return false;
}
if (lua_pcall(L, 0, 1, 0) != 0) {
wxLogError("Failed to execute save file: %s", lua_tostring(L, -1));
lua_pop(L, 1);
return false;
}
// Result should be a table
if (!lua_istable(L, -1)) {
wxLogError("Save file did not return a table");
lua_pop(L, 1);
return false;
}
// Restore globals
int count = 0;
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
if (lua_isstring(L, -2)) {
const char* key = lua_tostring(L, -2);
// Set the global
lua_pushvalue(L, -1); // duplicate value
lua_setglobal(L, key);
count++;
}
lua_pop(L, 1); // pop value
}
lua_pop(L, 1); // pop table
wxLogDebug("Restored %d globals from %s", count, filePath);
return true;
}
bool LuaPersistence::loadState(lua_State* L, const std::string& filePath) {
return loadGlobals(L, filePath);
}
} // namespace wherigo