Files
wx_wherigo/LUA_PERSISTENCE.md
2026-02-14 09:47:27 +01:00

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:

  1. Recursive traversal of Lua tables
  2. Type detection for different Lua types
  3. Escaping of special characters in strings
  4. Reference handling by Lua itself when loading

Deserialization

  1. Save file is loaded as Lua code (luaL_loadstring)
  2. Execution returns a table (lua_pcall)
  3. Table entries are restored as globals
  4. 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

  1. Function Callbacks are not saved (OnClick, OnEnter, etc.)

    • These remain defined in cartridge code
  2. Circular References are cut off during recursion

    • Max depth: 10 levels
  3. Metatables are lost

    • May need to be reset after loading
  4. Timer Status is saved, but Remaining may 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)