StupidRPG: Save, Load, & Undo

Most text adventures (that I’ve played, at least) don’t feature the auto-save ‘checkpoints’ common in modern videogames. They do include SAVE and LOAD commands which work exactly as you’d expect, but they also typically provide another command for state management: UNDO.

In a singleplayer, turn-based game, UNDO is a pretty straightforward feature to implement. There are a few different approaches, but I’ve elected to leverage the savestate functionality for efficiency’s sake. More on that in a bit.

SAVE

The save command in SRPG is handled primarily by the ECS. Since Everything is an Entity, I can simply loop through all the entities, building a new object which can be stored in JSON format. From there, it can be saved in the browser’s local storage or downloaded to the user’s computer.

Not every piece of data on an object is needed for savestate. Most callbacks don’t change once they’re set, and many object attributes are static as well. Accordingly, each Component can specify the persistent attributes that should be included in the save process. For example, the Living component specifies that the HP attribute should be saved.

LOAD

Loading works exactly as you’d expect: saving in reverse. The JSON data is read back and the entity attributes are re-applied to the live versions.

UNDO

Undo is a combination of an internal save (before each action, save the current state) and load, allowing the player to jump back in time just as if they had manually saved and loaded. Effectively, it’s an automated Quick Save feature. Supporting additional steps of undo is possible, at the cost of additional memory and CPU time.

Because UNDO is a verb just like any other in the game, there are some interesting opportunities for integration into the mechanics and story. That’s a surprise for another day, though.

Next Steps

There are a few shortcomings of the current model:

  • Saving multiple levels of undo is not cost efficient. I need to do better profiling of the memory consumption of the game.
  • Much of the saved data is not actually changed from the base entity state. I could reduce the size of the save files by only saving attributes that have actually changed, but that means adding more tracking code.
  • Some circumstances need special handling. For example, what happens if the player uses UNDO directly after SAVE?
  • Entities have to be uniquely identified in order for loading to work properly. This isn’t currently a problem, but could become one in the future with dynamically generated entities.