There are a few more things I should say about implementing autosaving in Inform 7. In the previous posts I neglected to address how these techniques interact with the default verbs UNDO, SAVE, and RESTORE, which was a bit of an oversight. I’ll get around to that now, and then I’ll show you a couple of other neat things you can do with autosaving.
SAVE and RESTORE are of course the commands that let a player manually manage their progress. I don’t know the details, but SAVE creates a file that contains all the parameters of the game’s state (unlike the methods I’ve detailed in previous posts, which only store the parameters you feel like tracking) and RESTORE grabs such a file and uses it to recreate that state. These verbs do not care in the slightest about any external files the game might be referring to. They ignore those files completely.
So you can probably imagine that combining these methods could get a little hairy. Let’s say, even though Ryan Veeder’s Authentic Fly Fishing saved all my progress automatically, I decide to restore a save file from a week ago. For one thing, I’d actually lose a week of progress! The external files would still be there, though. Loading a saved game doesn’t change the external files in itself, but continuing to play that saved game would probably affect those external files, such that when I restart again, the game restores progress from after I loaded a save file and from the week I “lost”! The timelines would collapse!
Yes, that does make it sound pretty cool, but it could easily have disastrous consequences. It’s safer to simply disable the normal manual saving paradigm, so that my very servicable autosaving paradigm may do its job in peace. Here’s how I did that in Ryan Veeder’s Authentic Fly Fishing.
Part 1 - Prevent typical saving behavior Check saving the game: say "This game auto-saves your progress whenever you do anything progressive, so there's no need to save the game manually. And in fact you are not allowed to save the game manually at all!" instead. Understand "load" as restoring the game. Check restoring the game: say "This game auto-restores your progress whenever you open it, so there's no need to restore anything manually." instead.
We also don’t want these commands to appear as options when the game is over, so we need to modify the Final Question Options. (See Writing with Inform §11.6 if you’re not familiar with this.) This is what I did in RVAFF:
[The final question ("would you like to undo, restart, whatever") can only show up when you Restart Completely as far as I know. But if it does, we don't want you to be able to undo that. We don't want the restore option either, because this game doesn't use normal saved games.] To change final question options (this is the remove undo and restore from final question rule): choose row five in the Table of Final Question Options; [UNDO] blank out the whole row; choose row two in the Table of Final Question Options; [RESTORE] blank out the whole row;
The UNDO verb is also a little tricky. Just like SAVE and RESTORE, UNDO is utterly ignorant of any external files. So if I >TAKE BANANA and my possession of the banana is autosaved to such a file, when I >UNDO, the current session of the game may have forgotten that I ever touched the banana, but it’ll still be recorded in the external file, and it’ll be restored to me when I restart the game.
In Ryan Veeder’s Authentic Fly Fishing and similar games in the super-polite touchy-feely bleeding-heart bowling-with-bumpers design space that’s so popular nowadays, this isn’t a problem, because you always want to get the banana—that is, all the actions worth saving are positive actions, and negative game states are always reversible. UNDO is never necessary, or even desirable.
You might not want to make a game like that, though. You might want your player to have to live with the consequences of taking the banana! Well, Inform 7 will always let you use undo prevention, with this declaration:
Use undo prevention.
I didn’t feel the need to use this in RVAFF, and unless you have a specific design reason, you probably don’t need to either. But I’m not here to tell you what you should do. I’m here to tell you what you can do.
You can let the player name stuff in the game! Here’s the code from RVAFF related to letting you decide what color your jacket is. (The code for letting you name the cat is basically identical.) First, we introduce a text called “jackethue,” and we make it so the game displays that text as part of the name of the jacket, and understand it as referring to the jacket.
If the jackethue of the jacket is “” (that is, an undefined text), that means the jacket hasn’t been given a color yet, so we can use that in conditionals as a way of saying “if the player hasn’t colored the jacket yet.”
The jacket is wearable. The jacket has a text called the jackethue. Understand the jackethue property as describing the jacket. The jackethue of the jacket is "". Rule for printing the name of the jacket: if jackethue of the jacket is "": say "jacket"; otherwise: say "[jackethue of the jacket] jacket". Description of the jacket is "It's a lightweight [jackethue of the jacket] jacket.[if location is E1a][paragraph break](As long as we're in the cabin, I guess you can change this with the command RECOLOR JACKET.)"
Now we need to give the player an opportunity to change that color. The main way to do this is to hijack the command line when the player examines a not-yet-colored jacket:
Jacketcoloring is a truth state that varies. Jacketcoloring is false. Instead of examining the jacket while jackethue of the jacket is "": say "It's a simple lightweight jacket. What color is it?[paragraph break][italic type]Please enter the color of your jacket[roman type]."; now jacketcoloring is true; After reading a command while jacketcoloring is true: now jackethue of the jacket is the player's command; now jackethue of the jacket is jackethue of the jacket in lower case; choose row 1 in Table of Customization; now content entry is "[jackethue of the jacket]"; split up the jacket name; write File of Customization from Table of Customization; say "Your jacket is [jackethue of the jacket]. Is that correct?[paragraph break]>"; now jacketcoloring is false; loop jacket color consent; reject the player's command; To loop jacket color consent: if player consents: say "Gotcha."; otherwise: now jacketcoloring is true; say "Okay, let's try this again. What color is your jacket?";
The autosaving happens at choose row 1 in Table of Customization; / now content entry is “[jackethue of the jacket]” and then write File of Customization from Table of Customization. (“split up the jacket name” was a later addition, and we will explain it later.) The Table of Customization is just a single column of text entries that RVAFF uses to save custom data and some other stuff. It looks like this:
Table of Customization content "" "" "" "" ""
For about 30 rows.
When “loop jacket color consent” comes in (more precisely, when I ask “if player consents:”), the game will only accept a yes or no answer. If the re-printing of the jacket’s new description is unacceptable, the player can answer no and try again.
If the player decides to change the jacket’s color again later, that’s possible (as long as you have the jacket handy and you’re in room E1a.) (“E1a” is the interior of the player’s cabin.)
Recoloring is an action applying to nothing. Understand "recolor jacket" as recoloring. Check recoloring: if location is not E1a: say "I have no idea what you're talking about!" instead; if the jacket is not visible: say "I can't see any jacket." instead. Carry out recoloring: Say "Okay, what color is your jacket now?[paragraph break][italic type]Please enter the color of your jacket[roman type]."; now jacketcoloring is true;
Saying “now jacketcoloring is true” triggers the color-parsing and -saving routine above.
This is all we need to autosave the jacket color, but to autorestore it, we need this little rule:
A customization file rule (this is the set jacket color rule): choose row 1 in Table of Customization; now jackethue of the jacket is content entry; split up the jacket name;
Very simple! But what is all this “split up the jacket name” stuff?
Well, the phrase “Understand the jackethue property as describing the jacket” is very powerful in itself—it feels a bit magical that I, the player, can decide unilaterally that the jacket is “dark moss green,” and then type in “wear dark moss green jacket” and the game understands me! But because the jackethue property is the string “dark moss green,” that means the game won’t properly understand “dark green jacket” or even “green jacket.” So we have to split up the jacket name.
To do this, we create a number of other texts/properties that the game should understand as describing the jacket. For some reason, I made exactly eleven such texts/properties:
The jacket has a text called the jacketterm1. Understand the jacketterm1 property as describing the jacket. The jacketterm1 of the jacket is "". The jacket has a text called the jacketterm2. Understand the jacketterm2 property as describing the jacket. The jacketterm2 of the jacket is "". The jacket has a text called the jacketterm3. Understand the jacketterm3 property as describing the jacket. The jacketterm3 of the jacket is "". The jacket has a text called the jacketterm4. Understand the jacketterm4 property as describing the jacket. The jacketterm4 of the jacket is "". The jacket has a text called the jacketterm5. Understand the jacketterm5 property as describing the jacket. The jacketterm5 of the jacket is "". The jacket has a text called the jacketterm6. Understand the jacketterm6 property as describing the jacket. The jacketterm6 of the jacket is "". The jacket has a text called the jacketterm7. Understand the jacketterm7 property as describing the jacket. The jacketterm7 of the jacket is "". The jacket has a text called the jacketterm8. Understand the jacketterm8 property as describing the jacket. The jacketterm8 of the jacket is "". The jacket has a text called the jacketterm9. Understand the jacketterm9 property as describing the jacket. The jacketterm9 of the jacket is "". The jacket has a text called the jacketterm10. Understand the jacketterm10 property as describing the jacket. The jacketterm10 of the jacket is "". The jacket has a text called the jacketterm11. Understand the jacketterm11 property as describing the jacket. The jacketterm11 of the jacket is "".
I don’t know if it was necessary to explain that each of these jacketterms is “”, because I’m pretty sure all texts are “” by default, but whatever.
Splitting up the jacket name is simply a matter of transforming each word of the player-defined jackethue into one of these properties, as below. (But why do I start out by saying every jacketterm is now “”? Just to wipe clean any previous names, so if the player is renaming the “dark moss green” jacket to the “pale pink” jacket, “green” is no longer understood as describing the jacket.)
To split up the jacket name: now jacketterm1 of the jacket is ""; now jacketterm2 of the jacket is ""; now jacketterm3 of the jacket is ""; now jacketterm4 of the jacket is ""; now jacketterm5 of the jacket is ""; now jacketterm6 of the jacket is ""; now jacketterm7 of the jacket is ""; now jacketterm8 of the jacket is ""; now jacketterm9 of the jacket is ""; now jacketterm10 of the jacket is ""; now jacketterm11 of the jacket is ""; now jacketterm1 of the jacket is word number 1 in the jackethue of the jacket; now jacketterm2 of the jacket is word number 2 in the jackethue of the jacket; now jacketterm3 of the jacket is word number 3 in the jackethue of the jacket; now jacketterm4 of the jacket is word number 4 in the jackethue of the jacket; now jacketterm5 of the jacket is word number 5 in the jackethue of the jacket; now jacketterm6 of the jacket is word number 6 in the jackethue of the jacket; now jacketterm7 of the jacket is word number 7 in the jackethue of the jacket; now jacketterm8 of the jacket is word number 8 in the jackethue of the jacket; now jacketterm9 of the jacket is word number 9 in the jackethue of the jacket; now jacketterm10 of the jacket is word number 10 in the jackethue of the jacket; now jacketterm11 of the jacket is word number 11 in the jackethue of the jacket;
Phew!
I have one other type of autosaving I’d like to share with you. It’s something I completely ignored in RVAFF: Saving the player’s location.
It’s not that different from saving anything else. Remember from the first lesson that we have to represent data in a way that Inform 7’s external files can handle. So, probably a number. Let’s get that out of the way:
A room has a number called roomnumber.
Our internal table where we save the location and the declaration of the corresponding external file will look like this:
Table of Locationsaving datum (a number) 0 The File of Locationsaving is called "locationsave".
We’ll update that table and then rewrite that external file every turn, with the latest information about where the player currently is.
Every turn: choose row 1 in Table of Locationnsaving; now datum entry is roomnumber of the location; write File of Locationnsaving from the Table of Locationnsaving.
And to restore the player’s location when they load the game back up:
When play begins: if File of Locationsaving exists: [make sure we have the player's data] read File of Locationsaving into Table of Locationsaving; otherwise: write File of Locationsaving from the Table of Locationsaving; choose row 1 in Table of Locationsaving; let currentroomnumber be datum entry; repeat with zone running through rooms: if roomnumber of zone is currentroomnumber: move the player to zone, without printing a room description.
The part that starts with “repeat with zone running through rooms” means, look at every single room, and it if it’s been given the number we’re looking for, put the player there. We say “without printing a room description” because the standard beginning-of-play machinery will print a room description for us after this custom stuff is done.
All that’s left to be done is to give every room a roomnumber. We talked about this already when we talked about autosaving inventory: Everything that can be saved needs a unique identifier to put in our tables. But I forgot to tell you a little trick you can do in these cases. This isn’t necessarily an autosaving trick; it has lots of uses.
Let’s say that, like me, you list all the rooms in the game and their connections before you do anything else. You have a bunch of code like:
Skull Chamber is a room. Mirror Zone is north of Skull Chamber. Snake Area is west of Mirror Zone and northwest of Skull Chamber. Snake Tunnel is down from Snake Area.
For this autosaving stuff to work, you need to add “Roomnumber of Skull Chamber is 1. Roomnumber of Mirror Zone is 2.” So tedious, especially for big maps. I only put four rooms in that example, and I’m already bored.
But you can make Inform 7 do the work for you:
When play begins: let num be 1; repeat with chamber running through rooms: say "Roomnumber of [chamber] is [num]."; increment num;
Now when you run your game, it’ll start out by generating a bunch of sentences, one for each room (in the order you mention them in the source code), each with a unique roomnumber. Of course, you have to then paste all those sentences into your source code for them to do anything. But think of all the repetitive code that you can let Inform 7 generate for you! Anyway, let’s get back to autosaving techniques.
Actually, I think I covered everything I wanted to cover with autosaving techniques. Let me know if you have any questions. Thank you for your interest.