gameplay/inventory.js

Gameplay — Save & Load

Save format v3, what is and isn't persisted, save/load API, and notes on area vs. global state.

Gameplay — Save & Load

The gameplay layer uses localStorage for persistence. Save format v3 captures all player progression state: inventory, quests, equipment, XP, skills, world flags. Area-specific entity state (enemy HP, entity positions) is not saved — enemies reset on area reload.

API

// In your GameManager / game.js
gameMgr.saveGame();   // serialize and write to localStorage
gameMgr.loadGame();   // read from localStorage and restore state

// Check if a save exists
localStorage.getItem('dn_save_v3') !== null;

The key is dn_save_v3. Each game typically uses its own key — update the constant in your game’s game.js to avoid collision with other DN games.

What IS Saved (v3 format)

{
  version: 3,

  // Player progression
  playerLevel: 4,
  playerXP:    250,
  skillPoints: 1,
  learnedSkills: ['iron_skin', 'quick_strike'],

  // Vitals
  playerHp:  80,
  playerMaxHp: 100,
  playerShield: { current: 40, max: 80 },

  // Equipment slots
  equipment: {
    mainhand: 'iron_sword',
    offhand:  'iron_shield',
    armor:    null,
    ring:     null,
  },

  // Inventory
  inventory: {
    items: { iron_bar: 3, fish: 5, wheat_seeds: 2 },
    currencies: { gold: 127 },
    stats: { attack: 8, defense: 5 },
  },

  // Quest state
  quests: {
    goblin_bounty: {
      status: 'turned_in',
      currentPhase: 0,
      objectives: [{ current: 3, count: 3 }],
    },
    fishing_quest: {
      status: 'active',
      currentPhase: 0,
      objectives: [{ current: 2, count: 5 }],
    },
  },

  // World flags (any key/value set via setFlag)
  flags: {
    bridge_rebuilt: true,
    met_elder: true,
  },

  // Player position (tile coordinates)
  position: { areaId: 'rpg_world', x: 15, y: 10 },
}

What Is NOT Saved

Not savedWhy
Enemy HP + positionsEnemies reset when the area reloads — stateless design
Chest loot stateChests (LootBehavior with oneTime: true) reset on reload unless you save the looted ID list explicitly
Farm plot growth progressGrowth uses Date.now() — if the area is reloaded, growth timer resets
Fishing pond stateResets on unload
NPC dialogue nodeAlways starts at root on next open

If you need any of the above to persist (e.g., a chest that stays empty after reload), save a flag: gameMgr.flags.set('chest_a1_looted', true) and check it in the area’s entity definition to spawn the chest in an already-looted state.

Save on Demand vs. Auto-Save

The framework does not auto-save — call gameMgr.saveGame() explicitly. Common save points:

  • After quest turn-in
  • After equipping an item
  • After leveling up
  • On area transition
  • On a dedicated “Save” button in the pause menu

The pause menu’s Missions tab includes a Save button by default in the rpg-test reference implementation.

Multiple Save Slots

v3 uses a single slot. To support multiple slots, namespace the key:

const SAVE_KEY = `dn_save_v3_slot${slotIndex}`;
localStorage.setItem(SAVE_KEY, JSON.stringify(saveData));

Upgrade Path

When the save format changes, bump the version and add a migration function:

function loadGame() {
  const raw = localStorage.getItem('dn_save_v3');
  if (!raw) return;
  const data = JSON.parse(raw);

  if (data.version < 3) {
    // migrate from v2 — e.g., shield was added in v3
    data.playerShield = { current: 0, max: 0 };
    data.version = 3;
  }

  // restore state from data...
}

Old saves with missing fields get safe defaults. Never break a player’s save on an update.