0 votes
by (180 points)

I noticed that saves doesn't work anytime, and I don't know if there's any way to solve this, or I'm just confused.
Example: I made a save data, then I went to Twine and added a new variable, when I loaded that old save, the variable didn't seem to exist or work as intended. I define my variables/arrays/etc on StoryInit.
The exact problem was something like this. I had a passage with: 

<<if $text == 1>> *text* <</if>>
<<if $text == 2>> *text* <</if>>
<<button "Continue">>
<<if $text == 2>>
<<goto tutoend>>
<</if>>
<<if $text < 2>>
<<set $text = 2>>
<</if>>
<<script>>
state.display(state.active.title, null, "back")
<</script>>

The point was displaying some text, and after the user click the "continue" button, refresh the page and display the other text. I ran the game, loaded and it didn't work. After that I just restarted the game and everything worked as intended, is there any problem with new variables being defined after a save?

1 Answer

+1 vote
by (44.7k points)
selected by
 
Best answer

When you load the save, it loads all of the variables that were stored in the game's history at the point where it was saved.  What's in your StoryInit passage is irrelevent when you load an old save.  This means that if you add more variables later, then they won't exist in old saves, which can cause problems if your code doesn't handle that properly.

You will either need to tell people that older saves are not compatible with the current version or you'll need to put in a bunch of checks to prevent possible errors.  Something like:

<<if ndef $someVar>>
	<<set $someVar = "default value">>
<</if>>

That checks to see that the variable is defined, and if it isn't, then it sets it to a default value.

by (159k points)

What's in your StoryInit passage is irrelevent when you load an old save.

a slight clarification:

While the story variable assignments in your StoryInit are irrelevent when you load an old save, any other code in that special passage (such as assignments to the setup special object) may still be relevant.

by (180 points)
Thanks, I will then put an advice to save in a special place where I can do the checks for newer versions.
by (44.7k points)
edited by

If you're doing that, then you might want to look into making a custom load function using Config.saves.onLoad.

In your JavaScript section you could put something like this:

Config.saves.onLoad = function (save) {
	/* isProperty: Returns whether Obj object has Prop as a property. */
	function isProperty(Obj, Prop) {
		if (!!Obj && typeof Obj === "object") {
			return Obj ? hasOwnProperty.call(Obj, Prop) : false;
		}
		return false;
	}
	/* Visited: Returns whether Psg passage was visited. */
	function Visited(Psg) {
		var i;
		for (i = 0; i <= save.state.index; i++) {
			if (save.state.history[i].title == Psg) {
				return true;
			}
		}
		if (isProperty(save.state, "expired")) {
			return save.state.expired.indexOf(Psg) >= 0;
		}
		return false;
	}
	/* CheckVar: Checks to see if Passage was visited, if it was then it
		sets VarName variable to Set, otherwise it sets it to Default. If
		Set or Default are "undefined" then it doesn't change the value. */
	function CheckVar(VarName, Passage, Set, Default) {
		if (Visited(Passage)) {
			if (Set != undefined) {
				save.state.history[save.state.index].variables[VarName] = Set;
			}
		} else {
			if (Default != undefined) {
				save.state.history[save.state.index].variables[VarName] = Default;
			}
		}
	}

	CheckVar("hasCoat", "Coat Room", false, true);
	CheckVar("hasHat", "Hat Check", "none", "white");
	CheckVar("hasHat", "Outside", "black");
};

Now, when you load a save, it will set $hasCoat to false if the player has visited the "Coat Room" passage, otherwise it sets $hasCoat to true.

It also sets $hasHat to "white", unless the player has visited the "Hat Check" passage, in which case it's set to "none", unless they've visited the "Outside" passage, in which case it's set to "black".  That's because the third CheckVar() call above overrides the previous one if "Outside" was visited, otherwise it leaves the current value alone (because the "Default" parameter is undefined).

That's just an example though.  Feel free to modify the above code if you need more complicated checks than that.

Hope that helps!  :-)

...