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:
const string&in name: the name of the stat.
var@ val: the starting value of the stat.
string text = "": the text template used to display the stat, where
%0is replaced with the value. If empty, it becomes"%0".stat_callback@ callback = default_stat_callback: the function that produces the stat's display text. If null,
default_stat_callbackis used.dictionary@ user = null: optional data to attach to the stat, copied into its
userproperty.
Properties:
string name: the name the stat is stored under in its set.
var val: the current value of the stat. It can hold any type a
varsupports, usually a number or a string.string text: the template used to display the stat, where
%0is replaced with the value. If empty when the stat is created, it becomes"%0".stat_callback@ callback: the function that produces the stat's display text. It defaults to
default_stat_callback, which fills%0intextwith the value.dictionary user: free storage for any extra data you want to attach to the stat, such as a sound to play or a category for display. The stat_set never reads it, and it is not saved by
serialize().
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.
stat_set();
stat_set(const stat_set&in other);
Arguments (2):
- const stat_set&in other: a set to copy.
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:
- const string&in name: the name of the stat to add.
- var@ value: the starting value for this stat.
- const string&in text = "": an optional text template used when displaying the stat, where
%0is replaced with the stat's value. If empty, the stat displays just its value. - stat_callback@ callback = null: an optional function that produces the stat's display text, for when a template isn't flexible enough. If null,
default_stat_callbackis used. - dictionary@ user = null: optional data to attach to the stat. The set never reads it, and it isn't saved by
serialize().
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:
- const string&in name: the name of the stat to be deleted.
Returns:
bool: true if the stat was successfully located and deleted, false otherwise.
deserialize
Loads stat values that were saved with serialize().
bool stat_set::deserialize(const string&in data);
bool stat_set::deserialize(datastream@ ds);
Arguments (1):
- const string&in data: the string returned by
serialize().
Arguments (2):
- datastream@ ds: a stream to read the serialized data from, starting at its current position.
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:
- const string&in data: the text to read.
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:
- const string&in stat_name: the name of the stat to check for.
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:
stat_sort_mode sort_mode = STAT_SORT_MODE_NONE: how to order the names (see
stat_sort_mode).const string[]@ sort_in_front = {}: names to move to the start of the list, in the given order, after sorting.
const string[]@ sort_behind = {}: names to move to the end of the list, in the given order, after sorting.
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.
void stat_set::mod(const string&in name, int64 value);
void stat_set::mod(const string&in name, double value);
Arguments:
const string&in name: the name of the stat to change.
int64/double value: the amount to add. Use a negative number to subtract.
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:
- const string&in name: the name of the stat to be updated.
- var@ value: the new value of the stat.
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:
- const stat_set&in other: the set to add.
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:
- const string&in stat_name: the name of the stat you want to access.
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.
- STAT_SORT_MODE_NONE: no particular order. This is the fastest, but the order can change as stats are added and removed.
- STAT_SORT_MODE_ADD_ORDER: the order in which the stats were added.
- STAT_SORT_MODE_VALUE: from the lowest value to the highest. Use this only when every stat in the set holds a number.
Funcdefs
stat_callback
The signature of a function that produces a stat's display text.
funcdef string stat_callback(stat@ stat);
Arguments:
- stat@ stat: the stat being displayed.
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"]));
}