3.8 KiB
3.8 KiB
Lua State Persistence / Save Game System
Overview
The Wherigo Player can save and restore the Lua state to preserve game progress.
How It Works
Automatic Save Locations
Game saves are automatically stored at:
- macOS:
~/Library/Application Support/wxWherigo/<CartridgeName>.save - Windows:
%APPDATA%\wxWherigo\<CartridgeName>.save - Linux:
~/.wxWherigo/<CartridgeName>.save
What is Saved?
Persistence saves:
- ✅ All Wherigo Objects: ZCartridge, Zone, ZItem, ZCharacter, ZTask, ZTimer, ZInput, ZMedia
- ✅ Object Properties: Name, Description, Active, Visible, Container, etc.
- ✅ Global Variables: All simple variables (Strings, Numbers, Booleans)
- ✅ Player Object: Position, inventory, etc.
- ✅ Nested Tables: Recursive serialization (up to depth 10)
What is NOT Saved?
- ❌ Functions: Lua functions cannot be serialized
- ❌ Userdata: C objects remain in original state
- ❌ Threads/Coroutines: Saved as
nil - ❌ Metatables: Not persisted
Usage in Code
Save Game State
// Automatic path (recommended)
wxGetApp().saveGameState("");
// Custom path
wxGetApp().saveGameState("/path/to/save.lua");
Load Game State
// Automatic path
wxGetApp().loadGameState("");
// Custom path
wxGetApp().loadGameState("/path/to/save.lua");
In-Game
- Menu → Save Game (Ctrl+S)
- Menu → Load Game (Ctrl+L)
Save File Format
The save file is a readable Lua file:
-- Wherigo Save State
return {
["Player"] = {
["ClassName"] = "Player",
["Name"] = "Player",
["ObjectLocation"] = {
["latitude"] = 51.5074,
["longitude"] = -0.1278,
["altitude"] = 0
}
},
["zitemLetter"] = {
["ClassName"] = "ZItem",
["Name"] = "Letter from Professor",
["Active"] = true,
["Visible"] = true,
["Container"] = <reference to Player>
},
-- ... more objects
}
Technical Details
Serialization
The implementation uses:
- Recursive traversal of Lua tables
- Type detection for different Lua types
- Escaping of special characters in strings
- Reference handling by Lua itself when loading
Deserialization
- Save file is loaded as Lua code (
luaL_loadstring) - Execution returns a table (
lua_pcall) - Table entries are restored as globals
- UI is updated via
notifyStateChanged()
Performance
- Saving: ~10-100ms for typical cartridges
- Loading: ~20-200ms (including Lua parsing)
- File size: ~10-500KB (depends on cartridge complexity)
Extensions
Custom Filters
// Save only specific globals
std::vector<std::string> myGlobals = {"Player", "zitemKey", "ztaskMain"};
wherigo::LuaPersistence::saveGlobals(L, "custom.save", myGlobals);
Auto-Save on Exit
// In cGame::OnClose()
wxGetApp().saveGameState(""); // Auto-save
Multiple Save Slots
std::string slot1 = wxGetApp().getAutoSavePath() + ".slot1";
std::string slot2 = wxGetApp().getAutoSavePath() + ".slot2";
std::string slot3 = wxGetApp().getAutoSavePath() + ".slot3";
Known Limitations
-
Function Callbacks are not saved (OnClick, OnEnter, etc.)
- These remain defined in cartridge code
-
Circular References are cut off during recursion
- Max depth: 10 levels
-
Metatables are lost
- May need to be reset after loading
-
Timer Status is saved, but
Remainingmay need recalculation
Debugging
Enable logging to trace save/load:
wxLogDebug("Saved %zu globals to %s", globals.size(), filePath);
wxLogDebug("Restored %d globals from %s", count, filePath);
Future Enhancements
Possible improvements:
- Compression of save files (zlib)
- Encryption (prevent cheating)
- Delta saves (only save changes)
- Cloud sync integration
- Automatic backups (keep 3 most recent saves)