Automatic progress saving [Snowman 1.3]

0 votes
asked Jul 8 by cwcorella (240 points)
reopened Jul 15 by cwcorella
Could someone please explain how to setup automatic progression saves in JavaScript? I'm writing a fairly long CYOA, and this would be incredibly useful to me. The system needs to save variables, player name, and start at the point they left off.

And would it be possible to specify a save folder? e.g. "C:\Users\DefaultUser\Documents\My Games\Game Name"

2 Answers

+1 vote
answered Jul 15 by TheMadExile (4,330 points)
selected Jul 15 by cwcorella
 
Best answer

SUGGESTION: It's probably best if you try to ask one question per thread.  Questions with multiple facets are fine, but asking multiple questions, even seemingly related ones, is problematic for this format.


The story.checkpoint(name) method suggested by Chapel does not create saves.  It adds entries to the browser's window history for that play session (only)—i.e. the player may navigate the checkpoint's via the browser's history controls during that play session, and that play session only.

If you're looking for a way to automatically save the state of a playthrough, so the player may come back to it later, then the closest thing Snowman offers built-in would be the story.save() method, which saves the current state to the URL as a hash.  Player's may bookmark that hash-added URL for later use.  For example, the following should create a link which adds the state hash to the URL:

<a href="javascript:story.save()">Save to URL</a> (create a bookmark after clicking this)

 

If you wanted something a bit more auto-magic, it's possible to write a module that uses Web Storage behind the scenes.  For example, the core module for the Story JavaScript (you must configure the save ID):

/*
	Snowman Web Storage Autosave Module
*/
(function () {
	/*
		ATTENTION: You must configure the save ID properly.

		This is the key used to store the autosave in Web Storage,
		it should be both static and unique to your project.  I'd
		suggest using something like a pen name combined with your
		project's name.
			e.g. 'glitch:AGameOfGnomes'
	*/
	var saveId = 'glitch:AGameOfGnomes';

	/*
		Module Functions.

		You probably won't need to muck with these.
	*/
	function hasWebStorage() {
		try {
			var store = window.localStorage;
			var tid   = '_sm_' + String(Date.now());
			store.setItem(tid, tid);
			var result = store.getItem(tid) === tid;
			store.removeItem(tid);
			return result;
		}
		catch (ex) { /* no-op */ }

		return false;
	}

	function hasAutosave() {
		return localStorage.getItem(saveId) != null;
	}

	function createAutosave() {
		localStorage.setItem(saveId, story.saveHash());
	}

	function restoreAutosave() {
		var hash = localStorage.getItem(saveId);
		if (hash) {
			story.restore(hash);
		}
	}

	/*
		If Web Storage is available: attempt to load an existing
		autosave at startup, set up an event listener to save each
		turn, and export a method to remove the autosave.

		NOTE: The event handling may need to change for the next
		major version of Snowman, as it looks like Chris is making
		significant changes there.
	*/
	if (hasWebStorage()) {
		var eventName = 'showpassage:after';

		if (hasAutosave()) {
			jQuery(document)
				.one(eventName, function () {
					restoreAutosave();
					jQuery(document).on(eventName, createAutosave);
				});
		}
		else {
			jQuery(document).on(eventName, createAutosave);
		}

		window.SWSAutosave = {
			remove : function () {
				localStorage.removeItem(saveId);
			}
		};
	}
})();

That will create an autosave each turn, which will automatically be loaded at startup.  I'm not 100% sure the resume code is what Chris would recommend, but it seems to work fine.  Also, the module will do nothing if Web Storage is unavailable.

To allow the player to remove the autosave, you would use markup something like the following:

<% if (SWSAutosave) { %><a href="javascript:SWSAutosave.remove()">Clear Autosave</a><% } %>

The wrapping logic is important there as the SWSAutosave object is only created if Web Storage is available.

commented Jul 15 by cwcorella (240 points)
That works wonderfully! I really appreciate you coding all that. It's precisely what I needed.

(You made a note regarding Chris' alteration of Snowman's event handling. If something breaks next update, I'll bring it up on the forums and hopefully it'll be a quick fix.)

I'll ask my follow up question in another post as you suggested.
0 votes
answered Jul 8 by Chapel (7,390 points)
In short, no browser is going to let you automatically (i.e. without user input) access files on the user's computer, for obvious reasons.   You can use the Story.checkpoint() method to save the story's state to the browser's local storage, though, if you're just looking to make auto saves.
commented Jul 10 by cwcorella (240 points)
I'm still fairly inexperienced with Twine, could you please elaborate on how to use Story.checkpoint()?

And how might I give the player control over that save? Is there an easy way for them to clear data while the .html file is open? e.g. through a keybinding.
...