0 votes
by (170 points)
I want to track player stats, with the idea that multiple stats such as "total kills" (across all games) and "kills" (in current game) are visible. Other stats would be "total games", "total captures", etc. Does anyone have a recommendation for how to implement such a system?

2 Answers

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

WARNINGS:
a. The following is one solution to your issue, a more experienced Javascript programmer may be able to supply you with a better one.
b. Some (older) web-browser may not support Local Storage, this document contains information about how to add support for them using one of two Polyfills. If you chose to add this support then the relevant Javascript found in that document should be added to the START of your Story Javascript area BEFORE any other Javascript you have in there.
c. Depending on the brand of web-browser being used data stored within the web-browser's Local Storage may be deleted if the web-browser's cache is flushed.

You can use Javascript to save and load data from your web-browser's Local Storage cache, such data is persisted between viewings of a HTML file. Then following Javascript code defines a GlobalStats object which contains the functionality required to access the Local Storage, this code should be placed within your Story Javascript area.

/* Define object to handle functionallity for Global Stats. */
if (! window.GlobalStats) {
	window.GlobalStats = {
		totalKills    : 0,
		totalGames    : 0,
		totalCaptures : 0		
	}
	
	/* Load the stored Global Stats data from local storage. */
	GlobalStats.load = function () {
		try {
			var data = localStorage.getItem('global-stats');
			if (data != null) {
				data = JSON.parse(data);
				this.totalKills    = data.totalKills;
				this.totalGames    = data.totalGames;
				this.totalCaptures = data.totalCaptures;
				console.log('Global Stats loaded');
			}
		} catch (ex) {
			alert('Global Stats: Load failed!');
			console.log('Global Stats: Load failed: ', ex);
		}
	};

	/* Save the Global Stats data to local storage. */
	GlobalStats.save = function () {
		var data = JSON.stringify({
				totalKills    : this.totalKills,
				totalGames    : this.totalGames,
				totalCaptures : this.totalCaptures
		});
		try {
			localStorage.setItem('global-stats', data);
		} catch (ex) {
			alert('Global Stats: Save failed!');
			console.log('Global Stats: Save failed: ', ex);
		}
	};

	/* Add the current stats to Global Stats then save to local storage.
	 * parameters:
	 * kills - the number of kills at the end of the game.
	 * captures - the number of captures at the end of the game.
	 */
	GlobalStats.addStats = function (kills, captures) {
		this.totalGames += 1;
		this.totalKills += kills;
		this.totalCaptures += captures;
		this.save();
	};

	/* Reset the Global Stats stored in local storage. */
	GlobalStats.resetStats = function () {
		this.totalGames    = 0;
		this.totalKills    = 0;
		this.totalCaptures = 0;
		this.save();
	};
	
	GlobalStats.toString = function () {
		return "GlobalStats: totalKills = " + this.totalKills
					+ " totalGames = " + this.totalGames
					+ " totalCaptures = " + this.totalCaptures;
	};
		
	GlobalStats.load();
}

NOTE: The above load() and save() functions use a 'global-stats' key to identify the item being stored within Local Storage, unfortunately this item is available to all HTML files accessed via the same domain. To stop your item accidentally being overwritten by other HTML files I strongly recommend that you give that key a more unique name, like one based on the name of your story.
eg. if your story is named 'My Adventure' then you should rename the 'global-stats' key in both the load() and save() functions to: 'my-adventure-global-stats'

The following is a four Passage example of how to use the GlobalStats object in a story.

1. Initialise the current $kills and $captures variables within your startup tagged special passage.

(set: $kills to 0, $captures to 0)

2. Create a Stats Passage which will be used to display the current and historical stats.
This Passage demonstrates how to output a value stored withing the GlobalStats object.

''Stats:''

Current:
Kills = $kills
Captures = $captures

History:
Total Kills = (print: GlobalStats.totalKills)
Total Captures = (print: GlobalStats.totalCaptures)
Total Games = (print: GlobalStats.totalGames)

3. Add the following to the story's starting point Passage.
It demonstrates how to call the GlobalStats resetStats() function, as well as how to refresh the Stats being shown on the current page. It also includes two links that allow you to change the number of current kills and captures, and a link to the GAME OVER scene. 

|stats>[(display: "Stats")]

(link-repeat: "Add a kill")[\
	(set: $kills = it + 1)\
	(replace: ?stats)[(display: "Stats")]\
]
(link-repeat: "Add a capture")[\
	(set: $captures = it + 1)\
	(replace: ?stats)[(display: "Stats")]\
]

[[Jump to end of current game->Game Over]]

(link: "Reset Global Stats")[\
	<script>GlobalStats.resetStats();</script>\
	(replace: ?stats)[(display: "Stats")]\
]

4. Create a Game Over Passage which is used to update the GlobalStats based on the current kills and captures. It also includes a link that restarts the game.

The GAME OVER screen.

(link: "Restart Game")[\
	(reload: )\
]

(print: "<script>GlobalStats.addStats(" + (text: $kills) + "," + (text: $captures) + ");</script>")

EDIT: fixed misnamed variable in catch block of Javascript example.

by (170 points)
Great, thanks so much. I'll let you know how I get on with this.
0 votes
by (159k points)

When you say "track player stats" do you mean.

a. A player can track only their own stats.
This is fairly easy to do using the web-browser's local storage cache.

b. A player can track the stats of all players (including themself)
This is a lot harder because it requires a central server which all the players access.

by (170 points)
I mean A. I recognize that I'll need to set up a server to track stats of all players. But I only wanted local stats.
...