Skip to content

Statistic Management (stat_set.nvgt)

Statistic Management

The stat_set include keeps track of named game statistics, such as kills, play time or a player's name, and makes them easy to update, display and save.

A stat_set holds any number of stat objects, each identified by a name. Every stat stores a value of any type in a var, plus a text template or callback that controls how the stat is displayed. Converting a stat to a string gives its display text, and converting a whole stat_set to a string[] gives the display text of every stat in the order they were added, ready to be shown in a menu or spoken.

Stats can be saved with serialize() and loaded with deserialize(), or written as simple name=value lines with serialize_linear().

Example

#include "stat_set.nvgt"

void main() {
	stat_set stats;
	stats.add("kills", 0, "Enemies defeated: %0");
	stats.add("deaths", 0, "Deaths: %0");
	stats.add("name", "Alex", "Player: %0");
	// Simulate a short game.
	for (int i = 0; i < 5; i++) stats["kills"]++;
	stats["deaths"]++;
	string[] lines = stats;
	alert("Statistics", join(lines, "\r\n"));
}

Classes

stat

A single named statistic in a stat_set.

stat(const string&in name, var@ val, string text = "", stat_callback@ callback = default_stat_callback, dictionary@ user = null);

Arguments:

Properties:

Remarks:

You normally don't create stats directly; use stat_set::add(), which creates the stat and returns a handle to it.

Converting a stat to a string gives its display text, from the callback. Converting it to a double gives its numeric value.

Stats support arithmetic with numbers, so you can write stats["score"] += 10 or stats["lives"]-- directly. The += operator also works with strings, appending to a string stat. ++ and -- keep whole number stats as whole numbers.

Comparisons and arithmetic on the stat itself, such as stats["score"] * 2, produce a plain number and don't change the stat.

Example:

#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stat@ score = stats.add("score", 0, "Score: %0");
	score += 150;
	score++;
	alert("Example", string(score) + ", doubled would be " + (score * 2));
}

stat_set

A collection of named statistics.

  1. stat_set();

  2. stat_set(const stat_set&in other);

Arguments (2):

Remarks:

The second constructor, and assigning one stat_set to another with =, create independent copies of every stat, so changing one set afterwards doesn't affect the other. The copies keep their original add order.

Stats are looked up by name with the [] operator, which returns a stat@ or null if there is no stat with that name.

Example:

#include "stat_set.nvgt"
void main() {
	stat_set current;
	current.add("coins", 10, "Coins: %0");
	stat_set saved = current; // Take a snapshot.
	current["coins"] += 5;
	alert("Example", "Now " + string(current["coins"]) + ", snapshot " + string(saved["coins"]));
}

Methods

add

Adds a stat to the set.

stat@ stat_set::add(const string&in name, var@ value, const string&in text = "", stat_callback@ callback = null, dictionary@ user = null);

Arguments:
Returns:

stat@: a handle to the newly added stat, or null if the set already contains a stat with that name.

delete

Deletes a stat from the set.

bool stat_set::delete(const string&in name);

Arguments:
Returns:

bool: true if the stat was successfully located and deleted, false otherwise.

deserialize

Loads stat values that were saved with serialize().

  1. bool stat_set::deserialize(const string&in data);

  2. bool stat_set::deserialize(datastream@ ds);

Arguments (1):
Arguments (2):
Returns:

bool: true if the data was read completely, false if it was missing, damaged or cut short.

Remarks:

Loading only updates stats that already exist in the set. Values for names that aren't in the set are skipped, so add every stat, with its text template or callback, before calling this. This also means that stats added in a newer version of your game simply keep their starting values when an older save is loaded.

Reading from a datastream lets you store the stats alongside other data in the same file.

Example:
#include "stat_set.nvgt"
stat_set@ create_stats() {
	stat_set s;
	s.add("level", 1, "Level %0");
	s.add("gold", 0, "%0 gold");
	return s;
}
void main() {
	stat_set@ game = create_stats();
	game["level"] = 4;
	game["gold"] = 1200;
	string save = game.serialize();
	stat_set@ loaded = create_stats();
	if (!loaded.deserialize(save)) {
		alert("Error", "The save is damaged.");
		return;
	}
	string[] lines = loaded;
	alert("Loaded", join(lines, ", "));
}
deserialize_linear

Loads stat values from name=value text lines, as written by serialize_linear().

bool stat_set::deserialize_linear(const string&in data);

Arguments:
Returns:

bool: true if at least one name=value line was found, false otherwise.

Remarks:

As with deserialize(), only stats that already exist in the set are updated.

Everything after the first = on a line is the value, so values may contain = themselves. Values that look like numbers, such as 42, -7 or 3.5, are loaded as numbers, and anything else is loaded as a string. Lines without an = are ignored, and both Unix and Windows line endings are accepted.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set settings;
	settings.add("difficulty", "normal");
	settings.add("volume", 0);
	settings.deserialize_linear("difficulty=hard\nvolume=-10\n");
	alert("Example", "Difficulty " + string(settings["difficulty"].val) + ", volume " + string(settings["volume"].val));
}
exists

Determine if a stat with the given name exists in the set.

bool stat_set::exists(const string&in stat_name) const;

Arguments:
Returns:

bool: true if a stat with the specified name exists in the stat_set, false otherwise.

get_stats

Gets handles to every stat in the set.

stat@[]@ stat_set::get_stats() const;

Returns:

stat@[]@: an array containing a handle to each stat in the set, in no particular order.

Remarks:

The handles refer to the stats themselves, so changing them changes the set. Use list() if you need the stats in a particular order.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("wood", 4);
	stats.add("stone", 9);
	stat@[]@ all = stats.get_stats();
	for (uint i = 0; i < all.length(); i++) all[i] += 1; // Give one of everything.
	alert("Example", "wood " + string(stats["wood"].val) + ", stone " + string(stats["stone"].val));
}
list

Gets the names of the stats in the set, optionally sorted.

string[]@ stat_set::list(stat_sort_mode sort_mode = STAT_SORT_MODE_NONE, const string[]@ sort_in_front = {}, const string[]@ sort_behind = {}) const;

Arguments:
Returns:

string[]@: the names of the stats.

Remarks:

Names in sort_in_front or sort_behind that aren't in the set are ignored. This makes it easy to keep a few important stats, such as a player's name, at the top of a display no matter how the rest are sorted.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("kills", 12);
	stats.add("deaths", 3);
	stats.add("assists", 7);
	stats.add("name", "Alex");
	string[]@ names = stats.list(STAT_SORT_MODE_ADD_ORDER, {"name"});
	alert("Example", join(names, ", "));
}
mod

Adds an amount to a stat's value.

  1. void stat_set::mod(const string&in name, int64 value);

  2. void stat_set::mod(const string&in name, double value);

Arguments:
Remarks:

Nothing happens if there is no stat with the given name.

This is the same as stats[name] += value, except that it doesn't fail when the stat doesn't exist.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("health", 100, "Health: %0");
	stats.mod("health", -25);
	stats.mod("mana", 10); // Ignored, there is no mana stat.
	alert("Example", string(stats["health"]));
}
reset

Clears all stats from the set.

void stat_set::reset();

serialize

Saves the values of every stat in the set to a compact binary string.

string stat_set::serialize();

Returns:

string: the serialized stat values, to be loaded with deserialize().

Remarks:

Only the names and values of the stats are saved. Text templates, callbacks and user data are not, because they belong to your code rather than to the player's progress.

String, whole number and decimal values are saved. Values of any other type are skipped.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("level", 3);
	stats.add("gold", 250);
	string data = stats.serialize();
	alert("Example", "Saved " + data.length() + " bytes");
}
serialize_linear

Saves the values of every stat in the set as readable text, one name=value line per stat.

string stat_set::serialize_linear();

Returns:

string: the stats as text lines, to be loaded with deserialize_linear().

Remarks:

This format is easy to read and edit by hand, which is useful for settings files or debugging, but it is larger than serialize() and can't store values that contain line breaks.

As with serialize(), only names and values are saved.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("difficulty", "hard");
	stats.add("volume", -10);
	alert("Example", stats.serialize_linear());
}
update

Updates a particular stat with a new value.

void stat_set::update(const string&in name, var@ value);

Arguments:
Remarks:

Nothing happens if there is no stat with the given name. Use add() to create a stat.

Operators

opAddAssign

Merges another stat set into this one with +=.

stat_set@ stat_set::opAddAssign(const stat_set&in other);

Arguments:
Returns:

stat_set@: this set.

Remarks:

For each stat in the other set, a stat with the same name in this set has the other value added to it, and a stat that doesn't exist yet is copied over along with its text template, callback and user data.

This is handy for adding the statistics of one round to a player's totals.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set totals, round;
	totals.add("kills", 10, "Total kills: %0");
	round.add("kills", 3);
	round.add("headshots", 1, "Headshots: %0");
	totals += round;
	string[] lines = totals;
	alert("Example", join(lines, ", "));
}
opImplConv

Converts the set to a list of display strings.

string[]@ stat_set::opImplConv() const;

Returns:

string[]@: the display text of every stat, in the order the stats were added.

Remarks:

Each entry is what you get by converting that stat to a string, so it uses the stat's text template or callback. This makes it simple to show all statistics in a menu, a list box or a single alert.

Example:
#include "stat_set.nvgt"
void main() {
	stat_set stats;
	stats.add("level", 5, "Level: %0");
	stats.add("class", "ranger", "Class: %0");
	string[] lines = stats;
	alert("Character", join(lines, "\r\n"));
}
opIndex

Allows for the easy access of stats with [...] syntax.

stat@ stat_set::opIndex(const string&in stat_name) const;

Arguments:
Returns:

stat@: a handle to a stat with the given name, or null if no stat could be found.

Properties

size

Returns the number of stats in the set.

int stat_set::size;

Enums

stat_sort_mode

The orders that stat_set::list() can return stat names in.

Funcdefs

stat_callback

The signature of a function that produces a stat's display text.

funcdef string stat_callback(stat@ stat);

Arguments:

Returns:

string: the text to show for the stat.

Remarks:

Pass a stat_callback to stat_set::add() when a text template isn't flexible enough, for example to format a number of seconds as minutes and seconds, or to choose singular or plural wording.

The callback is called every time the stat is converted to a string, including when the whole set is converted to a string[].

Example:

#include "stat_set.nvgt"
string format_time(stat@ s) {
	int seconds = int(s.val);
	return "Time played: " + (seconds / 60) + " minutes, " + (seconds % 60) + " seconds";
}
void main() {
	stat_set stats;
	stats.add("time", 754, "", format_time);
	alert("Example", string(stats["time"]));
}

Global Properties

default_stat_callback

The display callback given to stats that are added without one.

stat_callback@ default_stat_callback;

Remarks:

By default this is a function that replaces %0 in the stat's text template with its value.

You can assign your own function to change how every stat added afterwards is displayed when no callback is passed to stat_set::add(). Stats that were already added keep the callback they were created with.

Example:

#include "stat_set.nvgt"
string shout(stat@ s) {
	return s.text.format(string(s.val)).upper();
}
void main() {
	@default_stat_callback = shout;
	stat_set stats;
	stats.add("greeting", "hello", "Message: %0");
	alert("Example", string(stats["greeting"]));
}