Save Game

by Ron Gilbert
Oct 19, 2015

For the first 9 months of this project, Savegame was my evil nemesis. It taunted and poke at me, refusing to ever heed enough to grant me the slightest satisfaction of how well the engine was advancing. Savegame would would pop up and say "How are you going to save that?", and I would shrink down and reply "I have no idea." My only victory over Savegame was my ability to lock it up in a cage buried deep in the dark dungeon of my mind and pretend it wasn't there, tricking myself into believing the incessant howling was just the wind on a stormy night.

I knew I needed to confront this demon. When the cage could no longer hold the beast, it was going to be a battle that one of us would leave either dead or tamed.

The morning of the battle was as any morning was. Nothing about it hinted the confrontation that could — only hours later — lay waste to everything within it's reach.

It began as an idea: "Hey, wait... what if I..." and that slowly formed into a plan. Some might call the idea cheating, but honor in war is a myth; you do what you can to win and this was no different.

My idea was a weapon, a special weapon that not only could, but would, mean victory and the defeat of my nemesis, Savegame.

The problem with savegames in adventure games is a vast amount of information that needs to be saved. Adventure games aren't divided up into "levels" and I deplore the concept of "check points", especially in adventure games. Players should be able to save whenever they want and expect to be back exactly where they left off upon load.

The issue isn't file format. It's not about whether you store it in JSON, XML or raw binary. The file format is the easy part.

What is hard is iterating deep into the recesses of the game and gathering up every scrap of data that might need to be saved. It's not just a matter of saving some global variables that depict the state of the game, it's the much more complex matter of tripping through the animation system, remembering each frame an actor or object is displaying. Going through all the objects in the game and saving off their state and the state of every local variable they contain.

The way the Thimbleweed Park Engine works, is that each room in the game has a piece of code called the enter code:

Nickel <- {
    function enter() {
        // Code that inits the room when entering
    }
    // More stuff
}

This code is called whenever a room is entered or given focus. It's responsible for setting up the state of any objects or actors that might be dependent on an external state of the game, as well as starting any ambient animations or sounds.

The enter code goes back to the SCUMM system and I've always found it a convenient way to organize the state of a room. It keeps all the code about setting up the room in one place, rather than spreading it around various object and other rooms in the game.

When you enter the Nickel, the enter code looks at the state of the game and decides if Natalie is there and then places her in the right spot. It does the same thing for the photocopy machine and the stack of newspapers.

And this was my idea, my +5 to cleaving idea that would finally vanquish my persistent foe.

When a game loads, I just re-enter the room. It saved me (no pun intended) from having to save a huge minutiae of detail about the current room and, more importantly, the state of all the multi-tasking scripts that might be running. The enter code would just restart them as needed.

All I needed to do was save off five pieces of information. 1) Global variables, 2) Local variables for each room, 3) local variables for each actor, 4) Internal state of each actor and 5) Internal state of every object.

On load, I would just re-run the enter code for the room the player was currently in and everything would be set back the way it was.

Step one proved to be a tad difficult. There are a lot of global variables, most of which are internal to the Squirrel interpreter. I didn't need to save any of these, I just needed to save the global variables related to the game and I had no way to tell the difference. So, I did what I probably should have done to begin with, I moved all the game global variables in to a table called "g".  Rather than "talked_to_sheriff" it was now "g.talked_to_sheriff."

All the rooms, objects and actors are also in the global namespace to make it easier for the gameplay programmer and I didn't need want to iterate through them from here, that would come later.

The other issue I faced was the data contain in each variable. Saving ints and floats is easy, but saving variables that possible contained actors, objects or rooms was a lot more complex.

Actors are defined as follows:

ray <- {
        name = "Ray"
        fullname = "Agent Ray"
        detective = YES
        ate_hotdog = NO
        ordered_hotdog = NO
        picked_up_tape = NO
        flags = GIVEABLE|TALKABLE
        // more stuff
}

actorCreate(ray)

The actorCreate(ray) command takes the table 'ray' and creates an internal actor and associates the table with it.  Later on in the code, something like this might happen:

last_actor = ray

And here is the problem. Tables are saved as references, so the variables last_actor and ray really point to the same table, they are not copied on assignment.  But, if the savegame system serialize both of those variables, they would become two completely different tables on load, not what we want.

Internally, actors (plus objects and rooms) are given a unique int id when they are created that the system uses to reference them, this is automatically added to the actor's table and called _id. This allows the engine to know the actor that a table is referring to. The problem is, I can't save those _id's out, because they are assigned at run time, and if the order of the actor assignment was changed during a patch, the numbers would all be wrong, not to mention that if a new variables were added during the patch, it would be erased on load. One of the main criteria for the savegame system is that it must survive patches and updates.

So what I really needed to save was the name of the table (ray), so when the savegame system de-serialized last_actor, it knew it was the actor ray and could just point to that master table again.  The same is true for objects and rooms.

Since the engine keeps a list of all the actors, rooms and object independent from the tables in the code, after saving the global variables, I iterated through each of them, saving off the variables found in each table and also some internal state information that is not saved in the tables, things like x,y position, touchable, facing direction, etc.

All this information is saved in a JSON savegame file that looks like this:

{
currentRoom: "Diner"
gameTime156.52
inputState: 16
savetime: 1445006566
selectedActor: "ray"
version: 0
actors: {
    bankmanager: {
        _dir: 2
        _pos: "{540,11}"
        _roomKey: "Bank"
        defaultVerb: 3
        detective: 0
        flags: 131072
        name: "Mr. Cruffman"
        sawRaysBadge: 0
        sawReyesBadge: 0
        dialog: NULL
        last_room: {
            _roomKey: "Bank"
        }
    }
...

The savegame system knows when it sees an entry like last_room, it should look up the master table for Bank and place it in the variable last_room.

When loading, it serialize all the data, merging it with existing variables or creating new ones if they don't exist, then it re-enters the room specified in currentRoom.

I also created two global functions...

function preSave() {
}
function postLoad() {
}

That get called, just in case there ends up being a complex situation that that needs attention. So far, there has been none, but the hooks might be useful.

I don't need to save the state of the sound system, because most sounds are restarted in the enter code and this reduced the complexity of the task immensely. When I implement the music system, I will need to save it's state because music sits above the current room, but that is a task for another day and shouldn't be too hard.

There are two types of multi-threaded functions in the game: local and global. Local multi-threaded functions are killed when you leave a room and restarted when you enter, so I didn't need to save these. Global multi-threaded functions needed to be saved, and I didn't (and still don't) have a good way to save these. It's not just a matter of saving a few local variables and the PC, there might be a complex nested call stack, each with it's own set a local variables and enclosures and it all needs to be saved and then recreated on load. I looked at the Squirrel source code and I don't see an easy way to serialize and recreate a running thread.

The good news is that we don't use global scripts very often and in each of those cases, they can be done in a different way, so I opted to just not save global threads. Problem solved. I'm sure it could be solved if I wanted to bang my head against the problem for a few weeks, but it's just not worth it.

There are two downsides to my "enter the room on loaded" scheme.

1. If an actor is walking across the room when you save, and then you load, they will stop walking. I don't see this as a huge downside. Chances are you're loading the game a few hours, it not days, later and I don't think you'll remember where they were headed. The enter code restarts any NPC, so they will behave normally.

2. You can't really save in the middle of cut-scenes as that would require you know where actors were walking and the exact state of the entire animation system at that moment, precisely what I was trying to avoid.

My solution to that problem was to silently save the game right before a cut-scene, and then if you save during the cut-scene, it actually saves the game saved right before. The downside is you might loose a little progress, but we're trying to keep all cut-scenes to less than 10 seconds, so you won't really loose much, and once again, I don't think most players will even notice and it saves a ton of time on my end that I'd rather be using for other things.

And that's the save game system.

Tada!

- Ron



Reader 1 - Oct 19, 2015 at 14:09
"we're trying to keep all cut-scenes to less than 10 seconds"
good decision

Fred - Oct 19, 2015 at 18:06
Yes, this warms the cockles of my heart.

Lorfarius - Oct 19, 2015 at 14:21
When this project is done you should seriously considering putting all this stuff in a book!

Kevin Drum - Oct 19, 2015 at 14:23
I look foreward to one day manipulating the save game file to create strange scenarios.

currentRoom: "LunarSurface"
hasSpaceSuit: 0

mwahahahaha

Chris - Oct 19, 2015 at 14:23
I thought managing state was supposed to be tricky?

Brian Gilbert - Oct 19, 2015 at 14:23
Bonus: if I'm watching a cutscene I want to see again, I just save and reload while watching it!

Rmn - Oct 19, 2015 at 14:29
Very nice piece of engineering the game. Thank you, Ron, for sharing!

I have two questions:

1. Is your engine able to save mid-dialog? I don't mean in the middle of talking, but if you are in the middle of a dialog puzzle, can you save to try out a few things?

2. Just a gimmick, but would it be possible to restore cutscenes by storing an "offset" of the cutscene progress and upon loading the save state right before the cutscene, just fast forward until where the player decided to pause the game and save explicitly?

Once again, I really like your solution and I am just being curious about the corner cases here. : )

Ron Gilbert - Oct 19, 2015 at 14:33
1 - Internally, dialogs are treated as cut-scenes, so you can't save mid-dialog, but I might change that. I don't think it will be hard to restore dialogs given that they stop at discrete nodes.
2- That would be really hard. "offset" is a complex issue.

Rmn - Oct 19, 2015 at 14:38
Thank you for clearing this up. I always find it fascinating how head-ache provoking  these "everyday" issues like "just save the game!" can become in the individual situations.

Lars R. - Oct 19, 2015 at 17:14
How about "fast forwarding" up to the saved point in (game) time (i.e. the "offset") with – say – 5x animation speed or no delays at all? Could make a cool effect also.

Davide - Oct 19, 2015 at 17:45
Or just, simulate it without the need to render frames.
That is, call your global Update() multiple times (if you use fixed time steps) up to the desired offset,
bypassing your rendering stage and any synchronization with clock.

Davide - Oct 19, 2015 at 17:52
Of course, offset could be just the name of the last discrete event in the cut-scene.
So start from the beginning of the cut-scene, and Update() until the named event
(e.g. if the event "4th-sentence" does not exist anymore because of a patch, do not skip).

bottlerocketee - Oct 19, 2015 at 14:33
Why are you still struggling with save games after so many adventure games you programmed yourself?

Ron Gilbert - Oct 19, 2015 at 14:34
Because the underlying tech changes.

Christian - Oct 19, 2015 at 14:44
I have experimented with Lua and the Pluto persistence library. It can recursively go into all tables and even save bytecode associated with a running function. This would never work with patching because using pluto the potentially broken game code would become part of the savefile! So I was really looking forward to this blog entry. Thanks a lot for writing it!

Your approach is pure genius. The exact kind of compromise only experience gains you. Instead of solving hard, and ultimately pointless and problems you cut the problem exactly at the right part. Really clever!

Ron Gilbert - Oct 19, 2015 at 14:49
Squirrel has the ability to serialize the same way, but as you point out, it won't survive patching. The SCUMM save games couldn't really survive patching either. We had to be very very careful about any change made to the game and invalidating save games. Adding a single variable in the wrong place would destroy your Monkey Island saves.

LogicDeLuxe - Oct 20, 2015 at 12:18
Was there actually any patch released which changed a SCUMM script? The only official SCUMM patches I'm aware of, change the executables only.

Tobias - Oct 20, 2015 at 16:51
Now you know why.

Natalija - Oct 19, 2015 at 14:48
I don't really understand why you can't save the game at the exact second where you were before saving it..

Wisse Stolk - Oct 19, 2015 at 16:22
Because that would mean there is always an immense amount of data to save. If during an update of a patch you would change any working of any of this data, the savegame gets corrupted.

TL;DR more specific saving needs more data: more data means more ways for things to go bork!

Uli Kusterer - Oct 20, 2015 at 10:43
The heart of the problem is you can't just save all objects in the game, because then you'd save a copy of the entire game. A copy of the entire game means if a bug is fixed, all the savegames would be copies of the unpatched game and you'd still have the bug.

Also, you can't save all of the data in the game because some of it is related to things that are only true for the current time.

E.g. if you remember that the room you get to when going through the "shop door" is the "shop inside", you remember at which point in memory (RAM) that other object was loaded. Now if you shut down your computer and restart it and then start the game again, it might get loaded into a different location in RAM (e.g. because you viewed a website before you played the game the first time, and that used up the memory the game is now in after the restart). So you have to be very careful which parts you save.

Some things you can just change (e.g. save the room's name and then when loading look up what address the room with that name was loaded to), but others you can't. There are many special cases. Thimbleweed Park is working on a shoestring budget, so they take shortcuts so they don't have to figure out a special way to handle each issue, if there is one way that covers 90%, and the 10% are what people rarely do anyway.

It gets even worse when you consider things under the operating system's control. I.e. a savegame should depend on what sound card is attached or how many screens. If you just saved out the entire game frozen in time, then tried to continue it on another computer, it would suddenly find a completely different sound card is installed. Maybe it's stereo and mono, but the game is still feeding it only one channel audio ...

It's like running across an open field and suddenly being teleported into a forest. You'd run into a tree. That's what you're doing with a game, so you kinda need a way to tell it "you've just been teleported". In most cases, you can just tell them, they'll look around, reorient themselves and then go on. But if it's in the middle of a step or they've been running (i.e. cutscene), you can't stop them in mid-air, and can't abruptly decelerate them. There are solutions to work around that, but it's much easier to just restart at the beginning of the step.

Does that in any way help explain it ... ? :-)

Mike - Oct 20, 2015 at 13:12
I enjoyed your analogies :)

I ran into this issue with a game I developed recently, but since its visibility was low I never got beyond the brute force method of saving. This means I ran into the very problems you described :P

Basically, you can't just "image" the whole game, but instead you have to create a system that backs up the game on command.

Zombocast - Oct 19, 2015 at 14:48
I like the SawRayesBadge: 0 part. I remember you commented that the dialogs will change if they already spoken to one of the other playable characters. Its neat to see the implemented code line for that.
PS: thanks for using comments in your code!

Augusto - Oct 19, 2015 at 14:58
I guess this will be a silly question , but anyway...

Doesn't squirrel model a virtual machine? How about serializing the virtual machine (maybe gzipped), and just restoring it?

Uhm... yeah, and reloading all native objects  (the ones created by C++ code). If squirrel functions use dependency injection (or a helper function to get the native objects), that should be OK...

But squirrel threads would be part of the VM serialized and restored, and also the execution context...

Ron Gilbert - Oct 19, 2015 at 15:00
Yes, you could do that (with a lot of work), but the save games won't survive patching.

Augusto - Oct 19, 2015 at 15:10
Yup, there you have it. A silly question it was. Thank you! :)

Carlo Valenti - Oct 19, 2015 at 15:08
Can you give some example of global multithreaded functions? Thank you

Estranged2 - Oct 19, 2015 at 15:17
Are there NPCs that move about more, like Weird Ed in MM or the Stewardess in Zak? If there are such NPCs, isn't it important to know where they are headed and what they are trying to do?

Ron Gilbert - Oct 19, 2015 at 15:20
There are, and it is important the remember them, but they are tracked at a different level. They don't move around rooms when they are not not current. Actors didn't in Maniac Mansion either, it just used timers and then placed them in the rooms as you entered them. Same with Monkey Island.

Christopher Griffin - Oct 20, 2015 at 09:26
DOH!  Now the magic is gone, Ron!  I thought those little people really lived in my C64 and were coming to get me, now I realize they were all just actors in your coded play. sobbing uncontrollably

Kenney - Oct 19, 2015 at 15:28
Foe VANQUISHED!

Mattias Cedervall - Oct 19, 2015 at 15:42
Ron, great work taking care of the not awesome demon, but why don't you "simply" change Squirrel if you have the sourcecode?

Ron Gilbert - Oct 19, 2015 at 15:44
Change it to do what?

Mattias Cedervall - Oct 19, 2015 at 16:24
"I looked at the Squirrel source code and I don't see an easy way to serialize and recreate a running thread."

So it will provide an easy way for you. Thank you for your comment, Ron! :-)

Ron Gilbert - Oct 19, 2015 at 16:28
It wasn't designed for that and bending it to my will was going to be too much work. As with most problems and solutions, anything is possible, it's just tradeoffs in how much work it is. This wasn't worth it.

Mattias Cedervall - Oct 19, 2015 at 16:36
I understand. Thank you so much for your answer, Ron! :-)

Theo - Oct 22, 2015 at 15:51
Have you tried hitting on the head with a golf club?

Kim Jorgensen - Oct 19, 2015 at 15:44
Why is the "detective" element defined and persisted for actors? Is that expected to change during game play?

Ron Gilbert - Oct 19, 2015 at 15:45
No, but it has to be set somewhere.

Kim Jorgensen - Oct 19, 2015 at 16:00
OK, but why put it in the save game? Are the actors entirely created from the data in the save game as opposed to using the name of the actor to look up the actor definition?

Ron Gilbert - Oct 19, 2015 at 16:06
The entire actor table is saved, the engine can't pick or chose what data to save, otherwise the engine becomes locked to the game. The overhead of saving a few variables that never change is very low, so why not save them and save the work of having to pull them out.

Kim Jorgensen - Oct 19, 2015 at 16:18
Makes sense, thank you. I guess that it could be made generic if there was a concept of static and dynamic data. But you are right, why bother

Benoit Fouletier - Oct 21, 2015 at 22:47
Doesn't Squirrel have a concept of code-metadata (like C# attributes or Java decorators)? You could flag serializable variables that way. I understand it probably doesn't matter to you if the file size is low, just curious.
Though, if you have a "fixed variable", like I don't know, the actor's name, and for some reason you need to change its default value in an update, wouldn't the savegame value get in the way?

T. Benjamin Larsen - Oct 19, 2015 at 15:49
I suspect you might be secretly annoyed about the cut-scene "cheat". (We all love a 100% technically tidy solution). I believe however that it will make a lot of sense from a gamer's point-of-view. It's reminiscent of how some TV-shows rewinds the last scene after the commercial-break. It will probably be a lot better than a "perfect" solution showing the last part of a conversation for a split second for instance...

Uli Kusterer - Oct 20, 2015 at 10:54
Or how some Podcast players will actually not save your playback position exactly, but go back by ~ 1 minute, so you can hear the start of the sentence again.

Yeah, I think of the few players who actually save in the middle of a cut scene, most will appreciate the "previously on" feature. :-) Cutscenes are just for watching, so it's not a problem. At worst, one could break up a longer cutscene into several shorter ones to "fake" being able to save halfway through it. Conversations on the other hand get really annoying if you have to navigate the entire tree and give a bunch of answers again, just because you took a call on your iPhone which quit the game to make room for the phone app in memory.

So as long as conversations can be restored or are very short, barely anyone will notice.

Rado - Oct 19, 2015 at 16:05
For grown-up* adventure fans, it's important to have cloud auto-save, because those people try to squeeze some play time inbetween (family) responsibilities, probably while traveling, on different devices. Thanks.

Ron Gilbert - Oct 19, 2015 at 16:08
The game will have autosave and to the cloud if the platform supports it, which everyone does as of now. The other advantage of not doing a raw state engine dump is that the save games are cross platform (assuming the platform can support it).

Matt - Oct 20, 2015 at 10:00
I'm glad to hear there will be autosaves. I plan to get so immersed in the game I forget saving is an option. :)

On that note, if you use a single autosave file, make sure to save somewhere else then replace the existing autosave. I've made this mistake with my own programs and no one enjoys a corrupted save file if the game crashes during the save process and there isn't a backup.

Uli Kusterer - Oct 20, 2015 at 10:58
So that means that as long as I play on e.g. a Mac and an iPhone, I can access my cloud saves on both because it both goes in iCloud, but I won't be able to have them synced between my Mac and Android phone because Apple doesn't license iCloud to others, right?

Ron Gilbert - Oct 20, 2015 at 11:05
Well, that depends. If you got the Mac version from the Max App store, then you could share saves, but if the Mac version came from Steam, you probably can't. It's a very screwed up situation. There might be a 3rd party that officers cloud space for stuff like this, in that case every device would be able to load a save game from another. It's something I will look into to, but cloud saves bring a whole new set of headaches due to syncing. It sounds simple, but it's fraught with edge cases. Clayton and I did cloud saves for Scurvy Scallywags and it was non-stop pain. You play the game on device A while on the train without internet, then you go home and play the game on device B, then you turn on device A and it syncs. What happens? It turns into a sea of confirmation dialogs that just confuse players (myself included).

Asterisk - Oct 21, 2015 at 22:07
I can't wrap my head around implementing cloud syncing at the application level, anyway.  For those of us who use standalone cloud sync tools, like Dropbox or OwnCloud, can you just make the save directory a configurable option?  That way, only iOS, with its user-inaccessible filesystem, will be the odd man out, and the rest of us can just specify a save directory that's already being synced independently.

Steffen - Oct 20, 2015 at 11:14
Please give an option to choose between cloud-autosave and local-autosave. Or perhaps do both.
I don't use anything in clouds!
And autosaves must be also possible if the machine is offline.

Kai - Oct 19, 2015 at 16:19
Just wondering: If you ever need to release a patch, and that patch introduces a new variable, would you;

- Include code to do a silent in-memory conversion from a version 1 save game into a version 2 save game (and more functions to go from version 2 to 3, and from 3 to 4, which you would call incrementally if required)?

- Convert all existing save games to the newest version when TP is first started after patching?

- Or would you simply always use default values for the missing variables in old save games - could be tricky if they depend on other values?

Mattias Cedervall - Oct 19, 2015 at 16:34
Here's what I suggested earlier:

Maybe the save file can have a version number
corresponding to the version of the game
installed? Here's some items from version 1:

1 sword
1 hat
1 phone

The game is updated to version two and now
one object is removed and another is added:

1 2 sword
1 2 hat
1 phone
2 joystick

Now the items are marked as they are in version
1-2, but the phone doesn't have a 2 mark which
means version 2 of the game will not try to load
that item in the players inventory.

If you get a save file from a friend that is using
version 2 of the game, then your version 1 of
the game will simply ignore the version 2
items because the code said to ignore items
higher than the game's current version number.

If you remove a room in version 2, then the
characters can maybe get moved to a default
location or something like that?

I don't know if any of this makes sense, I
just want to try to help out.

Kai - Oct 19, 2015 at 16:42
I think you'd still need code to convert the save game internally because the V1 save game will still say "1 sword" and not '1 2 sword". You can update it when you overwrite the old save game, though. Also, I guess it'd be OK if a V1 installation refuses to load a V2 save game. The other way round is what's more important IMHO.

A function that, when loading a game, calculates an initial value for those variables that were introduced post-V1 could work as this function could use all existing variables. It would also force the programmer (a.k.a. Ron) to think hard about all new variables and what value it should have for existing save games, thus ensuring that it's not an afterthought.

Mattias Cedervall - Oct 19, 2015 at 17:14
I've only taken a beginners course in Ada so unfortunately I can't say I'm a programmer although I'm a computer technician. Save games are complicated and from now on I will always wonder how each game I play defeated the totally evil demon Savegame.

Uli Kusterer - Oct 20, 2015 at 11:07
I presume that the game first loads all its objects (i.e. a "clean game") and then applies all the changes from the savegame file. So if some static value or pre-calculated value is added in V2, it would just suddenly be in there. Similarly, if switching on the light switch causes the lamp to show lit up, as soon as the savegame restores the lightswitch to "on", it'd have the same effect (it might just skip the long cutscene that usually happens during gameplay because it knows it's just restoring a save).

So any added state will just magically "be there" in the ideal case. Bigger problem is if isDetective has been saved to disk and then a bugfix requires for it to be "yes" on one character. But you could work around that by just renaming it to isADetective and then ignoring the value.

So, new dynamic values just get the state that they'd have a new game, which is fine because they weren't there when the user first played them. New dynamic values that depend on other state get updated as they're loaded anyway. New static values are "just there". It's actually quite beautiful !

Ron Gilbert - Oct 19, 2015 at 17:10
If you're running v1 of the game, it should not load save games from v2. Too much bad stuff can happen. They need to be backward compatible, but not forward.

Mattias Cedervall - Oct 19, 2015 at 17:16
I see. Thank you so much for your reply, Ron.best! :-)

Huxi - Oct 19, 2015 at 16:21
Sorry, Ron, but both downsides sound suspiciously like features to me.

Thanks for another interesting update!

Derrick Reisdorf - Oct 19, 2015 at 16:22
What's the "GG" in GGDictionary, e.g., stand for?
What about "sq" in sq_poptop(vm)?  hsq?
vm?  surely not Virtual Machine?  This is some kind of generic or dynamic object type?

I guess I could find maybe out if I read into Lua or Squirrel.  Indulge me, if you don't object to my laziness.

Ron Gilbert - Oct 19, 2015 at 16:26
GG is for GrumpyGamer. Those are all functions and class in my library. sq_ is for squirrel. Those functions came from the squirrel source, I didn't name them. vm does stand for virtual machine.

Darkstorm - Oct 19, 2015 at 16:29
Curious, if an expendable inventory item is used that triggers an action (say for example an NPC walking towards a door and unlocking it), how would your system handle a save done while the NPC is walking towards the door?  Would all such actions be considered cutscenes and the savegame start you from before the items use, or would the door just be unlockd already on reload and the NPC wherever he was supposed to be after that event?

Ron Gilbert - Oct 19, 2015 at 16:40
That's a really good question. We tend to make stuff like that quick cut-scenes just to reduce the complexity of the player messing with stuff while the NPC is walking to the door. In a case where we wanted to let the player move around, we'd have a variable that understood the action was in-progress. It's not ideal, but it happens very rarely.

Darkstorm - Oct 19, 2015 at 17:08
Thanks for the explanation.  I guess a scenario where you both want the player to have control AND have an expendable trigger would be very rare as the player might mess up the required action and need to retry, which I suppose make it far more useful to write the situation such that it makes story sense to have a re-usable item, or have the item unlock an dialogue tree option instead which persists and can be used to make the NPC repeat the action on failure.

Christian - Oct 20, 2015 at 01:28
Remember the post about a bus and an elevator? David Fox was surprised how many here enjoyed and appreciated the complexity of the bus driver interaction.
I think that situations like the player being able to mess around with e.g. a NPC walking are similar. It's fun to try to mess with them and if the designers included special text or script actions for such situations it feels really nice to discover.
So of course, use cutscenes to solve these design issues whereever you want, but in case you have to do lots of extra work for workarounds and more complex scripts (also in the testing department) to handle such situations, there will be people who really appreciate it. It makes the game somewhat more alive and rememberable, just like the bus driver in Zak.

Martin -enthusi- Wendt - Oct 19, 2015 at 17:27
Hehe, great.
I did it exactly like that in 'Caren and the Tangled Tentacles' (there is a Youtube vid of the released version in the link :)
There it meant to also store the 'last room' since the 'entry behavior' for each room depends on where we came from.
It boiled down the savegame data to all global variables.

Arto - Oct 19, 2015 at 17:53
Just an idea: would it make sense to have automatic saves in addition to manual saves? You know, if there would be a blackout or a system crash, it would be nice to have a saved game that is more fresh than the one player saved 3 hours ago. If I understood correctly, it shouldn't be a big task.

Ron Gilbert - Oct 19, 2015 at 17:58
There are autosaves. They happen on rooms transitions, so the most you'll loose is the time you've spent in the current room. I might also autosave when you lose window focus, but I don't know about that one yet. I might add others as the need arises (like idle time).

stderr - Oct 19, 2015 at 18:37
Does it count as "window losing focus" when you switch to another app on a phone/tablet?
It would be nice if you could switch app, without losing your progress in the game.

J - Oct 20, 2015 at 07:45

Paulup - Oct 19, 2015 at 18:15
Having thought about it a lot, I think the best way to solve the save game problem would be:
- Pick up Squirrel
- Use Squirrel on complex nested call stack
- Talk to JSON
- Give complex nested call stack with Squirrel in the middle to JSON
- Pick up save game

Done.

Ron Gilbert - Oct 19, 2015 at 18:20
Wow. I'm an idiot for spending all that time on my savegame code when the solution was right in front of me. I've failed as an adventure gamer.

Paulup - Oct 19, 2015 at 18:50
The save game solution was actually hidden in the "Dance Instructions" that David Fox gave you early on in production, if you recall.

DZ-Jay - Oct 22, 2015 at 06:33
That was brilliant! :)

Mr. Gilbert, don't feel too bad.  You may be very good at writing them, but playing adventure games is a whole other kettle o' fish! LOL!

    -dZ.

Zak Phoenix McKracken - Oct 19, 2015 at 18:47
Excellent!
I remember the same first "downside" occurred in Maniac Mansion and Zak: if a character was walking and I wanted to save the game, when I reloaded the game, the character was standing, faced towards its direction. I think this is not an issue. Even better: it's in perfect 1987 style!

Steve - Oct 19, 2015 at 19:32
Should have just used GameMaker ;)

Zarbulonian - Oct 19, 2015 at 21:05
Hurray! I'm glad you sorted this out.

Shawn Swift - Oct 19, 2015 at 22:50
Just a suggestion, but from a skeuomorphic (I learned a new word!) interface standpoint, having the save game background be a highway sign doesn't make a lot of sense. An evidence folder on the other hand would make a whole lot of sense and it could still be the same basic shape.

Ron Gilbert - Oct 19, 2015 at 22:59
Once again, it's all temp stuff. I just threw it together in 5 minutes. Although, I do disagree about the evidence folder, it's too much of a cliché for me. As I've mentioned before, this really isn't a game where you go around collecting evidence. The story is much deeper and way more complex than that. The highway sign actually has a lot more to do with the story than evidence does, but the save game screen (like so much shown here) will probably be something complete different by the time the game ships.

DZ-Jay - Oct 22, 2015 at 06:36
OK, then how about using a floppy disk icon for the saving function?

*ducks*

Dan - Oct 22, 2015 at 18:46
I would vote for it, too - especially for the iconic 5.25 inch floppy disk. There have already been discussions in the internet about what would be a meaningful replacement for the floppy disk icon, representing the 'save' operation, since the youths of today hardly know about the floppy disk. That's really sad! Therefore I say: *Save the floppy disk icon!*

Dan - Oct 22, 2015 at 19:02
The perfect homage would be an animation of a floppy disk, getting inserted into a Floppy Disk Drive, which then makes the typical FDD sounds while the LED lights up. I really miss that! But of course I know about your tight time schedule, so I won't suggest this.

Derrick Reisdorf - Oct 23, 2015 at 16:42
Hmmm...  The game is set in 1987.  Definitely the era of 5 1/4" floppies.
However, the game itself seems like it would be a game run (or installed) from a CD-ROM.  No way this game could even seemingly fit on a stack of 5 1/4" floppies.  :)

I'm sure whatever save screen Ron goes with will end up being neato.

Cheeseness - Oct 19, 2015 at 23:03
That sounds like an approach that will cover all the important bases (who wants to save during cutscenes anyway?). Thanks for sharing!

For my (very simple) game, I ended up storing all player actions that are relevant to a puzzle (aside from moving between scenes, whether an action does something or not is determined by whether it's part of a puzzle) and writing that out to file as the current state of in-progress puzzles plus a list of completed puzzles. When the player loads, I have my StateManager class walk through all of the actions implied by the loaded puzzle states to arrive at the game state the player left at, then I plonk them down in the scene they were in when they saved.

That assumes that the order that the puzzle states were saved in is appropriate, which feels fine since it's whatever order the player completed them in, plus I can force/respond to specific ordering by shuffling my puzzle dependencies. It also means I don't need to care about inventory items and stuff since they're effectively cosmetic, with direct puzzle dependency (eg: throwing a snowball requires the "pickup snow" puzzle to be completed first) filling in the logic.

Peter Campbell - Oct 19, 2015 at 23:23
w00t!  Images of some new, never before seen backgrounds!  =)

One question, probably a very silly one, but is there any risk for TP of there being a so-called "memory leak", which usually occurs in huge open world games like Skyrim and Red Dead Redemption where the save file becomes tremendously bloated in size from having to remember every little change in game data and it causes the game to lag worse and worse the more you play the game?

Ron Gilbert - Oct 19, 2015 at 23:25
Not really. Since the save game is saving everything all the time, it won't grow hardly at all over time.

Grafekovic - Oct 20, 2015 at 11:28
I haven't noticed the lag you mentioned in open world games. Since there is no really persistent open world, the save game data could't grow over time, it just saves the current progress. You can drive over as many lights, signs and people, when you drive around the block, everything will be in its place again.
Maybe this changes with the "powah of da cloud"!

Ashley B - Oct 19, 2015 at 23:54
You mentioned in an earlier post that you also save the music in the background?  Unless music is pivotal to a puzzle, is there any reason to save the point the background music is at?  Couldnt you just reset it each time?

Big Red Button - Oct 20, 2015 at 02:35
In my opinion, since you don't reload a savegame in such a frequency, saving the state of the music would be more relevant in case of leaving a room and re-entering it shortly after.

Zak Phoenix McKracken - Oct 20, 2015 at 02:51
...Good Morning... (cough cough)....

Is there a hot coffee or tea dispenser in the game? I need it just now...

LogicDeLuxe - Oct 20, 2015 at 12:47
If there isn't, someone will release a Hot Coffee patch and start an affair. :D

Tunrip - Oct 20, 2015 at 05:28
Sweeeet! Good going, that man!

Regarding the "saving during cut scenes", I think your approach is actually beneficial. If I save during a cut scene, I'd probably want it to start over again anyway :)

Marco Lizza - Oct 20, 2015 at 05:57
Really good job, Ron. However, I think I'm going to miss not being able to restore the actors "walking state" (i.e. they will be still upon restoring a saved state).

Mattias Cedervall - Oct 22, 2015 at 03:48
I will miss it.

Andreas - Oct 20, 2015 at 07:27
Hi Ron,
is a cutscene predictable, if you know the initial variables at the point in time when it triggered?
If so, couldn't you save the (let's call it) 'cutscene ratio' to restore the actual state? (we've seen 0.732 of this cutscene...)

Marco Lizza - Oct 20, 2015 at 08:58
... if cutscenes were movies that would work, indeed...

Francesco Favia - Oct 20, 2015 at 08:53
Of course, my fault,  
But when I encounter such technical posts that I understand at the 50% ....
I always feel the need to ask what The Secret of Monkey Island is... O_O

Zak Phoenix McKracken - Oct 20, 2015 at 09:12
!!! I think I had a dejà vu...

longuist - Oct 20, 2015 at 09:14
Knowing the secret would be great, but will i understand it?
Btw, RSS is not firing...

Lucas - Oct 20, 2015 at 10:07
I wanted to read this post since development day 1. Thank you, this is exactly what I expected. I always wanted to know how this is done in adventures games since saving everything is impossible. Also I really appreciate you letting us know the limitations it has, like, if you were walking when you load it won't be, or the silent save before the cut scene.

What about auto-save? modern gamers are just used to close the game and it re-loading where they left automatically.

Gv - Oct 20, 2015 at 10:41
I have very little experience programming games, I tend to use engines like AGI or SCI from Sierra, and wondered how they saved data. For example AGI saves all the variables, but that is no problem because you only have 256 variables. But it saves also the state of the animations. Again not too hard I think. I used to think about all this because I created a system for making games in AGI with no need of programming, using only text commands. And the game created is stored in the savegame. It is a game that creates games so to speak. If you want to put a background you just type "put img one" for example. If you want to place a character "put char two" etc. It is like making a SCUMM game by using the SCUMM verbs, so to speak. The games created are really crude, but saves a lot of time, and since I don't do it professionally, who cares. In the website clicking my name you will see examples of them. (shameless plug)

Pavel - Oct 20, 2015 at 10:44
It is possible to store game scenarios as *data structures*, just as sequences of steps like "walk to X;Y", "say Z", "run animation A on object O", etc. Steps can be nested (Composite pattern) to provide more complex logic.
Benefits:
1. all verb processing, all character's movements, all cutscenes can be considered as such scenarios. It provides a conceptual simplicity since all these things are implemented as scenarios.
2. decoupling: scenario can be declared in one context and called in another whenever it is required.
3. multiple parallel scenarios can run simultaneously.

Each step knows how to serialize/deserialize its state. So it is possible to save and load game during cutscenes.

P.S. I always thought that it is great advantage of SCUMM-games that they could be saved at any moment. Very interesting how it was made.

Alessander - Oct 20, 2015 at 16:34
Ron, instead of messing with squirrels, you should have adopted a programming language from the save game  programming paradigm. Squirrels don't like saving games, and that's a fact.

Simon Simon - Oct 20, 2015 at 16:34
Thank you for yout post, Ron, this is very interesting read - I like your approach to *avoid* difficult save problems by clever engine and game design instead of implementing an extremely complex (and probably hard to test) save routine.
What I do not really understand is, what are global multi-tasking threads? You say you are able to avoid them completely, But for what  kind of game action those global scripts could be used or be helpful (despite the fact that you decided to not use them)?

I think of complex actions that span multiple rooms, like the puzzle in MI where the shop owner walks to the swordmaster, or the melting jug. I may be totally wrong. But I would appreciate if you gave an example situation.
SImon Simon

mr. T - Oct 20, 2015 at 16:35
The monster has been slain! Now the kids can have their turn and throw some rocks at the carcass and beat it with sticks. Hopefully it didn't get to have offspring before the fatal encounter...

Pretty hardcore post as well, thanks for explaining the case all in-depth. Though now I have a slight urge to go through the whole source and try to understand how it all fits together. The horrible pain of not knowing the unknown. Oh and the two downsides. I don't think them as downsides at all. It would be a bit weird I think, if the character would be moving right after loading a game. In fact I might experience that as a bug in the game... Also with the cutscene playback it is only common sense to get it play from the beginning no matter what (Of course for busy people it would be important to save the extra 4.324 sec it takes to wait for the whole cutscene to be played).

Steffen - Oct 20, 2015 at 18:29
Many thanks for your explanation as well as all the comments to user questions... I never thought about savegame problems but this was really interesting!

Flowo1974 - Oct 20, 2015 at 18:56
Hm.. No one commented on the the art in the save game "screen shots"...

Simon Simon - Oct 21, 2015 at 02:21
It is very promising and spectacular! But: new rooms - I better don't look too closely ;)

Daniel Wolf - Oct 21, 2015 at 03:28
Just a minor technical question: Does Squirrel actually add internal global variables during the runtime of the application, or is there a fixed (albeit cryptic) set of internal globals that Squirrel creates once at startup? -- If it's the latter, you could just collect the names of *all* globals immediately after initializing Squirrel, knowing that these are the ones you aren't interested in. Then you could use globals in your scripts as you originally did (without the `g` variable). On save, you'd just skip those variables you collected at startup.

That way, your scripts would be (marginally) easier to write. Plus, you wouldn't run into the problem of forgetting the `g.` prefix and thus having globals that aren't saved.

Topper - Oct 21, 2015 at 06:37
You're my hero, Ron Gilbert.

Christian - Oct 21, 2015 at 07:18
I understood only 25%-50% of this post/article, but still: fascinating! :-)
Regarding 'downside 1': Also, most players will intuitively save when their current character is standing still. So they won't notice/mind. :)

Arto - Oct 21, 2015 at 11:32
Walking when saved / standing still when loaded wouldn't be odd, if the character would be waiting for the player to return. When savegame is loaded, the character could look straight at the player, tapping leg, with hands on hips, and say something like "Where were you? I've been waiting for 22 hours for your return."

Zombocast - Oct 21, 2015 at 14:14
The movie Intersteller did that. I wont go into too much detail but that was my favorate part!

Zak Phoenix McKracken - Oct 22, 2015 at 03:32
Ahahahah of course, you are also able to compute in binary on-the-fly ?

Zombocast - Oct 22, 2015 at 09:36
How I Imagine "Detective Rayes" would welcome me back if the real time save load metric was implemented.

Warning Interstellar spoiler: https://youtu.be/nOipaf5Rt9o

Zak Phoenix McKracken - Oct 22, 2015 at 10:07
Great! Instead, here is my favourite part on Interstellar Movie:

(WARNING! HIGH LEVEL SPOILER!!!)
https://www.youtube.com/watch?v=iJio07EtKYc&feature=youtu.be&t=1m21s
(WARNING! HIGH LEVEL SPOILER!!!)

Zombocast - Oct 22, 2015 at 14:25
Your vision is much.more troubling, I may cry now lol.
Thanks for sharing!

hihp - Oct 21, 2015 at 16:24
Ron, I do wonder: You do save the state of EVERY room when you save a game, right? And that is why you don't have to care for any puzzle in another room -since that room's state is serialized as well in the save game, so its data is there and the room will be reinstated ad it was when the player last exited it? Is that how it works?

Which also brings me to another question: If you do an action that changes another room - e.g. opening a door by switching a lever, or removing an npc - do you change that room's data in memory, or will the code for the other room upon entering check for the lever state in the first room, and adjust the door accordingly?

Bill - Oct 21, 2015 at 17:30
Ran: Great post! A  fascinating glimpse into your development process. The first image, of the window titled "Save Game" with screenshots and metadata captions:"<date> at <time>", inspired me to humbly offer a suggestion: the verb interface being included in the screenshot strikes me as redundant. Could you just capture the content/"room" portion of the screen, or would that be more difficult to implement?
Just a thought (allegedly)... :)

longuist - Oct 21, 2015 at 19:43
You are probably right, but seeing the inventory may be useful?

Bill - Oct 21, 2015 at 23:41
Good [pint! I did not even consider that, as in the sample image, only the first inventory slot (FBI badge) is even partly visible in any of the saved game screenshots.

Derrick Reisdorf - Oct 23, 2015 at 16:46
They should at least move the timestamp to the top of each screenshot.

Mattias Cedervall - Oct 22, 2015 at 03:46
Who is Ran? Ron's evil twin?

Bill - Oct 21, 2015 at 17:46
"Arto"'s idea sounds humorous, but I do think the laugh potential of a single fourth-wall violation would notbe worth breaking game-world immersion, and a whole series of them (like on each load) would quickly get old, tiresome, and annoying. It might be a different matter if the fourth wall were consciously ignored or thrown out entirely and/or consistently throughout the game, like a running joke of the characters speaking directly to the audience. I just doubt that fits with the mood or feel Ron and company are going for.
As I have said before, I kind of think of this game like a point-and-click interactive X-Files episode.. the truth is out there!.

Zak Phoenix McKracken - Oct 23, 2015 at 04:06
Good morning everyone!
Thanks God It's Friday :
- a new weekend is coming
- a new TP podcast is in the air ready to be recorded

Ciao!

Carlo Valenti - Oct 23, 2015 at 14:34
Ciao!

Emmanuel - Oct 23, 2015 at 05:26
For several hidden object adventures that I shipped, I ended up saving the entire state of the game in a semi-creative way - in each room, when the game made changes (move an object to a different keyframe and so on), the native C++ engine stored the commands used internally, they were pushed to the saved game, and restored when reloaded. It worked great, and restored the game from any point, but it made patching difficult (new objects were OK, but existing objects could not have keyframes added or whatever to support a new puzzle), and more importantly, it saved the state of bugs together with the game. Once you hit a bug in the game, once you saved past it, you were screwed and had to start over.

Having this amount of state also made cloud saving impossible. Can you imagine merging that much data? Me neither, so those games didn't support cloud save.

It is a lot better to save the minimum amount of state and have an enterScene() put things in their place depending on the local and global variables - even if that means not restoring cutscenes in the middle. That's what I've done in later games - the C++ engine autosaved when you switch rooms (including cutscenes) and when you resumed the game, it reentered the room. It's less technically "perfect" but it is a lot better for both you and the end-user. And supporting cloud saving is a lot easier. Those games were set up so that variables could be merged either as "take the max of", "replace", or "let a function deal with it" but there was so little state that it was very simple to do.

I tried both on multiple shipped large games and the latter solution is the superior one.

dada - Oct 25, 2015 at 16:25
I hate like buttons but i'd like love buttons on these blog posts!

ole# - Oct 26, 2015 at 15:11
why dont you just disable saving while a cutscene is running ? lot of games do that

smisk - Nov 10, 2015 at 20:40
Super interesting article! This devblog has really made me aware of facets of game design and programming  I never think about. But mostly its just convinced me that I'll never be smart enough to make a game from scratch like that...
Keep up the good work!