Services/GameStats.cs

Static helper that lists stat and achievement identifier constants for the game and exposes thin wrappers to increment stats and unlock achievements via Sandbox Services Stats and Achievements.

Networking
using Sandbox.Services;

namespace BrickJam;

/// <summary>
/// Central catalogue of the s&box <see cref="Sandbox.Services"/> stat + achievement identifiers this game
/// reports, plus thin wrappers over <c>Stats</c>/<c>Achievements</c>.
///
/// Stats and achievements are tied to the LOCAL player's account, so these must run on the OWNING client -
/// host-side gameplay routes through <see cref="Player.TrackStat"/>/<see cref="Player.TrackAchievement"/>
/// (which are <c>[Rpc.Owner]</c>), and owner-side code (e.g. <see cref="Player.Kill"/>) can call here directly.
///
/// IMPORTANT: stats, achievements and leaderboards only register against a PUBLISHED game package - they must
/// be defined on the game's sbox.game dashboard to persist, unlock, and appear on leaderboards. In local play
/// the Services calls safely no-op (the package resolves to null), so this is harmless during development.
///
/// Leaderboards are configured on the dashboard FROM these stats (e.g. a "money_earned" leaderboard), so just
/// reporting the stats below feeds them - no extra code needed unless we add an in-game leaderboard panel.
/// </summary>
public static class GameStats
{
	// --- Stats (cumulative; each can back a dashboard leaderboard of the same name) ---
	public const string MoneyEarned = "money_earned";
	public const string LootCollected = "loot_collected";
	public const string LootSold = "loot_sold";
	public const string LocksPicked = "locks_picked";
	public const string LevelsCompleted = "levels_completed";
	public const string Deaths = "deaths";
	public const string Escapes = "escapes";
	public const string SavesLoaded = "saves_loaded";           // backs the stat-mode file_explorer achievement
	public const string LevelsEscapedUnique = "levels_escaped_unique"; // backs the stat-mode grand_tour achievement

	// --- Achievements (manual unlocks; the identifier must exist on the dashboard) ---
	public const string AchFirstSale = "first_sale";
	public const string AchEscape = "escape";

	// Newer batch. STAT-mode achievements (file_explorer/the_one_percent/grand_tour) auto-unlock on the
	// dashboard from their backing stat crossing a threshold - code only reports the stat. The rest are
	// one-shot EVENT unlocks fired from gameplay via Player.TrackAchievement. All idents must be defined on
	// the sbox.game dashboard to actually persist/unlock for a player.
	public const string AchFileExplorer = "file_explorer";        // STAT saves_loaded >= 50
	public const string AchThePercent = "the_one_percent";        // STAT money_earned >= 10,000
	public const string AchGrandTour = "grand_tour";              // STAT levels_escaped_unique >= 3
	public const string AchSlippedOnSoap = "slipped_on_soap";     // die in the Bathrooms within 30s of entering
	public const string AchLeftForDead = "left_for_dead";         // escape as the sole survivor
	public const string AchHoardersParadise = "hoarders_paradise";// extract with a full inventory
	public const string AchFullyLoaded = "fully_loaded";          // own every shop upgrade
	public const string AchOutsmarted = "outsmarted";             // lose a monster that was chasing you
	public const string AchGoodBoy = "good_boy";                  // summon Doob for the first time
	public const string AchBodyguard = "bodyguard";               // Doob takes a monster hit for you
	public const string AchManslaughterByProxy = "manslaughter_by_proxy"; // a nearby teammate dies while you (with Doob) live

	public static void Increment( string name, double amount = 1 ) => Stats.Increment( name, amount );

	public static void Unlock( string ident ) => Achievements.Unlock( ident );
}