starting playing the wherigo
Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
This commit is contained in:
228
main/src/lua/persistence.cpp
Normal file
228
main/src/lua/persistence.cpp
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user