-
-
Notifications
You must be signed in to change notification settings - Fork 92
Score Engine
PVSneslib includes a score management engine designed for arcade-style games where a player's score can reach large values.
A complete working example is available in
snes-examples/systems/scoring.
A score is represented by the scoMemory struct, which holds two 16-bit unsigned integers:
typedef struct
{
u16 scolo; // low counter — counts from 0 to 9999
u16 scohi; // high counter — counts from 0 to 9999
} scoMemory;The two fields behave like a two-digit decimal group in a larger counter:
-
scoloaccumulates points in the range 0 to 9 999. - When
scoloreaches or exceeds 10 000, the excess wraps back to zero andscohiis incremented by 1. -
scohialso ranges from 0 to 9 999 and is saturated at 9 999 — there is no further carry.
This gives a maximum representable score of 9 999 × 10 000 + 9 999 = 99 989 999.
The struct occupies 4 bytes of WRAM. Declare one or more of them as global variables in your.c file:
#include <snes.h>
scoMemory playerScore;
scoMemory hiScore;#include <snes/scores.h>scores.h is already pulled in by the master <snes.h> header, so in most projects you do not need to include it separately.
void scoreClear(scoMemory *source);Sets both scolo and scohi of *source to 0.
Example:
scoreClear(&playerScore);void scoreAdd(scoMemory *source, u16 value);Adds value (0–9 999) to source->scolo. If the result reaches or exceeds 10 000, the engine subtracts 10 000 from scolo and increments scohi by one. scohi is capped at 9 999.
Example:
// Award 250 points
scoreAdd(&playerScore, 250);
// Award 9750 points — if scolo was already 500, this pushes it to 10250,
// which wraps: scolo → 250, scohi → scohi + 1
scoreAdd(&playerScore, 9750);void scoreSub(scoMemory *source, u16 value);Subtracts value from source->scolo. If scolo goes below zero, the engine borrows from scohi (adds 10 000 to scolo and decrements scohi).
If scohi is already 0 and a borrow would be needed, the score saturates at {0, 0} — it never goes negative.
Example:
// Deduct 100 points penalty
scoreSub(&playerScore, 100);void scoreCpy(scoMemory *source, scoMemory *dest);Copies both scolo and scohi from *source into *dest. Use this to update the high-score record or to snapshot a score.
Example:
// Save player score as the new high score
scoreCpy(&playerScore, &hiScore);u8 scoreCmp(scoMemory *source, scoMemory *dest);Compares *source against *dest and returns:
| Return value | Meaning |
|---|---|
0x00 |
*source equals *dest
|
0xFF |
*source is greater than *dest
|
0x01 |
*source is less than *dest
|
The comparison checks scohi first; scolo is only compared when the high words are equal.
Example:
if (scoreCmp(&playerScore, &hiScore) == 0xFF)
{
// Player beat the high score — copy it
scoreCpy(&playerScore, &hiScore);
}void scoreToStr(scoMemory *source, char *buf);Converts *source to a fixed-width, null-terminated ASCII string and writes it into buf.
The output is always exactly 8 digits followed by a null terminator (9 bytes total):
HHHH LLLL \0
│ │
│ └─ scolo, zero-padded to 4 digits
└────── scohi, zero-padded to 4 digits
buf must point to a buffer of at least 9 bytes.
| Score | String written to buf
|
|---|---|
{ scohi=0, scolo=0 } |
"00000000" |
{ scohi=0, scolo=42 } |
"00000042" |
{ scohi=1, scolo=2345 } |
"00012345" |
{ scohi=9999, scolo=9999 } |
"99999999" |
Example:
char scoreBuf[9];
scoreToStr(&playerScore, scoreBuf);
consoleDrawText(10, 5, scoreBuf); // draw the 8-digit string on screenScore cap.
The maximum score is { scohi=9999, scolo=9999 }, displayed as "99999999". scohi is
saturated and will not overflow further; no undefined behaviour occurs if you keep adding points
beyond the maximum.
Updating the high score.
Call scoreCmp first, then scoreCpy only if the player's score is greater. This avoids needlessly overwriting the high score with an equal value.
Game Over reset.
Call scoreClear at the start of each new game to zero scolo and scohi. The high score variable should only be cleared when deliberately resetting records (e.g. a factory-reset option
in your settings menu).
Saving to SRAM.
scoMemory is a plain 4-byte struct. You can copy it into your SRAM save block with a simple memcpy or by copying the two fields individually — no special serialisation is required.