+1 vote
by (160 points)
edited by

Looking to create a New Game Plus for your Twine game / story? It's a very popular game feature, and it's perfectly doable with Sugarcube. 

Below is an example on how to do this, courtesy of TheMadExile. This existed on the old QA site, but I've reposted it here so people can take a look at it.

Post this in your Javascript:

/* New Game+ Handling Module */
(function () {
	/*
		The `storage` variable is a persistent instance of
		SugarCube's internal `KeyValueStore` API.  I've used it
		in this example, because it's both persistent and helps
		avoid key collisions with other stories.

		Any other persistent storage medium could be used.
	*/
	if (storage.has('NewGamePlus')) {
		/*
			Load the New Game+ data into a `setup` property and then
			remove it.
		*/
		setup.NewGamePlus = storage.get('NewGamePlus');
		storage.delete('NewGamePlus');

		/*
			Evaluate the `NewGamePlus` special passage, which will
			handle applying the New Game+ data to the new game.

			Errors will be caught and shown.
		*/
		try {
			Wikifier.wikifyEval(Story.get('NewGamePlus').text);
		}
		catch (ex) {
			Alert.error('NewGamePlus', ex.message);
		}
	}
})();


Add this to your passages or create them:

StoryInit

/* Values modified by a NG+. */

/*
	If $coins is undefined, initialize it to zero.
	
	Normally, it will be undefined at this point, however, under
	a NG+ start it will already have a value courtesy of the
	NewGamePlus special passage.
*/
<<if ndef $coins>><<set $coins to 0>><</if>>
/*
	Now add whatever the default value should be to the exisiting
	value—i.e. zero normally, something non-zero under a NG+.
*/
<<if ndef $blah>><<set $blah to 0>><</if>>
/* Values not modified by a NG+. */


NewGamePlus

/* If NG+ coins is defined, initialize $coins to its value. */
<<if def setup.NewGamePlus.coins>>
	<<set $coins to setup.NewGamePlus.coins>>
<</if>>

/* Other NG+ handling. */


/* Clean up setup.NewGamePlus. */
<<script>>delete setup.NewGamePlus;<</script>>


Part1

You have $coins coins.
$blah
[[Part2]] 

 

Part2

<<set $coins += 50>>
<<set $blah += 5>>

$blah is blah.

You have $coins coins.

[[End]] 

 

End

You have $coins coins.
----
Start a New Game+.  Normally, this would be on your end game passage.  This example only carries the player's coins over.
<<button "New Game+">><<script>>
var sv = State.variables;

storage.set('NewGamePlus', {
	// Carry $coins over.
	coins : sv.coins
});

Engine.restart();
<</script>><</button>>

 

1 Answer

0 votes
by (38.6k points)

Possibly a bit easier, you could use the $Achievments variable to store any information about achievements in the game or unlocking "new game +" using the <<remember>> macro (instead of the <<set>> macro).

To make that work optimally, first, in your StoryInit passage, put this:

<<if ndef $Achievements>>
	<<remember $Achievements = {}>>
<</if>>

That will make sure that the $Achievments variable is initialized as an object if it doesn't already exist, and then it will remember it even when a new game is started or loaded.

And then add this to your JavaScript section:

Config.saves.onLoad = function (save) {
	// Make sure Achievements are preserved, even if an old save is loaded.
	if (save.state.index >= 0) {
		var SaveVars = save.state.history[save.state.index].variables;
		if (State.variables.hasOwnProperty("Achievements")) {
			if (SaveVars.hasOwnProperty("Achievements")) {
				// Update Achievements if necessary.
				var Keys = Object.keys(State.variables.Achievements), i;
				for (i = 0; i < Keys.length; i++) {
					SaveVars.Achievements[Keys[i]] = clone(State.variables.Achievements[Keys[i]]);
				}
				State.variables.Achievements = clone(SaveVars.Achievements);
				$.wiki('<<remember $Achievements>>');
			} else {
				// Add Achievements.
				SaveVars.Achievements = clone(State.variables.Achievements);
				$.wiki('<<remember $Achievements>>');
			}
		} else {
			if (SaveVars.hasOwnProperty("Achievements")) {
				// Make sure saved Achievements are remembered.
				State.variables.Achievements = clone(SaveVars.Achievements);
				$.wiki('<<remember $Achievements>>');
			}
		}
	}
};

That will make sure that, even if you load an old save, it won't remove any data from the $Achievments variable.  However, it will change any values to match the (possibly older) saved values, thus it's best to only add properties to the $Achievments variable which will remain unchanged once set (e.g. if you add a property and set it to true, it will then it should always and only be set to true after that).

Now that you've done all that, marking that an achievement, like New Game+, has been unlocked is as easy as this:

<<remember $Achievements.newGamePlus = true>>

and you could check that using:

<<if $Achievements.newGamePlus>>
	/* code for if $Achievements.newGamePlus is true here */
<<else>>
	/* code for if $Achievements.newGamePlus is not set or is false here */
<</if>>

Just make sure that instead of using <<set>> when changing the $Achievments variable, you always use <<remember>> instead, and that will let you keep all of the data on the $Achievments variable across saves.

Enjoy!  :-)

Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...