Posted on May 10, 2017
An Overview of Save Games
In the old days, Sierra was brutal when it came to save games. Death was around every corner, and it was up to the player to remember to manually save progress. Although Cascade Quest still has a good amount of death, that kind of unforgiving behavior isn’t really acceptable these days. On top of that, save games must survive patching – another complexity that wasn’t really an issue back in the day.
Cascade Quest allows saving your game at almost any point. When this is done, the modifiable state of the world is serialized and saved to disk. This includes all global variables, all script variables, and all objects and their properties.
Changes to the game code can easily invalidate save games. Objects may be added or deleted, variables may be added or removed, and so on. Trying to reload an old save game with a changed codebase would create all sorts of problems.
Luckily, the way the game engine is set up, there is a limited amount of information that gets passed from room to room. When a new room is entered, all stateful scripts are unloaded, and then reloaded on demand. So the only state that gets transferred is the set of global variables. The new room is responsible for instantiating all objects that are relevant based on the global state.
And even among the global state, there is only a portion of that that is relevant persisted state – basically a set of global flags and numbers (that indicate if certain events have happened in the game, for instances).
Upon entering a room, this global state can be saved off somewhere. Then when the player saves a game, this data is saved alongside the rest of the save game data. If, when loading a save game, we see that it was for a different version of the game, we can instead load the global state that defined the game when the player entered the current room. The downside is that this puts the player back to when they entered the room (some UI will be needed to explain this, no doubt).
This is also a trivial way to implement autosave. Upon changing rooms, we save off the above-mentioned global state as the autosave. This does mean we need to be careful sometimes: there are cases when a player’s death is certain when they enter a room. In those cases, logic is needed at room startup to detect this and disable the autosave. These cases are rare, however.
Using that limited set of global state doesn’t completely absolve us of thinking about codebase changes affecting save games of course. Patches to the game can’t ever remove any global state – we can only add new flags or variables. So care must still be taken to have the new code correctly interpret the old code’s state. But a set of flags and variables is much easier to manage than an entire heap of instantiated objects and other state.