Files
2026-10-14 06:59:51 +02:00

126 lines
5.7 KiB
C#

namespace Campus_live_rewrite.api.server.Classes
{
/// <summary>
/// Which currency a game mode pays out in.
///
/// The client asks /api/storefronts/v2/balance to add currency and NAMES the currency
/// itself. Trusting that is how a completed Golden Trophy match paid out 1000 Dracula
/// Silver - Crescendo's currency. The room the player is actually in decides now; the
/// client's CurrencyType is only a fallback when the room is unknown.
///
/// Room names come from the live server (Room_server/rooms/search).
/// </summary>
public static class GameCurrency
{
/// <summary>
/// The one balance bucket this server keeps per currency, on the wire as "Platform"
/// (BalancePlatform.NonPurchasedNotUsableInP2P = -2, the reference's ALL_PLATFORMS). The
/// client holds a balance per (CurrencyType, Platform) and SUMS them, so every balance
/// frame - the GET, a purchase response, a balance add, the 61 push - has to name this
/// same bucket; one that says 0 (SteamPurchased) makes a phantom bucket that is added to
/// the real one for the rest of the session.
/// </summary>
public const int AllPlatformsBucket = -2;
/// <summary>Currency for a room name, or null when the room has no special currency.</summary>
public static CurrencyType? ForRoomName(string? roomName)
{
if (string.IsNullOrWhiteSpace(roomName)) return null;
// Room instance names arrive as "^RoomName".
string n = roomName.TrimStart('^').Trim();
if (n.Equals("Crescendo", StringComparison.OrdinalIgnoreCase))
return CurrencyType.DraculaSilver;
if (n.Equals("IsleOfLostSkulls", StringComparison.OrdinalIgnoreCase))
return CurrencyType.LostSkullsGold;
if (n.StartsWith("RecRoyale", StringComparison.OrdinalIgnoreCase))
return CurrencyType.RecRoyale_Season1;
if (n.StartsWith("Paintball", StringComparison.OrdinalIgnoreCase))
return CurrencyType.PaintballPaint;
if (n.Equals("LaserTag", StringComparison.OrdinalIgnoreCase))
return CurrencyType.LaserTagTickets;
// GoldenTrophy and everything else pay in Rec Center Tokens - which is what the
// Golden Trophy storefront (StorefrontType 102) actually prices its items in.
return CurrencyType.RecCenterTokens;
}
/// <summary>
/// STRICT balance-room whitelist: the ONLY rooms in which POST v2/balance may mint
/// currency, and which currency each mints. Anything not listed returns null and mints
/// nothing. Rec Center Tokens are deliberately absent - they are server/admin only.
///
/// Crescendo -> DraculaSilver (101)
/// IsleOfLostSkulls -> LostSkullsGold (100)
/// LaserTag -> LaserTagTickets (1)
/// </summary>
public static CurrencyType? ForBalanceRoom(string? roomName)
{
if (string.IsNullOrWhiteSpace(roomName)) return null;
// Room instance names arrive as "^RoomName".
string n = roomName.TrimStart('^').Trim();
if (n.Equals("Crescendo", StringComparison.OrdinalIgnoreCase))
return CurrencyType.DraculaSilver;
if (n.Equals("IsleOfLostSkulls", StringComparison.OrdinalIgnoreCase))
return CurrencyType.LostSkullsGold;
if (n.Equals("LaserTag", StringComparison.OrdinalIgnoreCase))
return CurrencyType.LaserTagTickets;
return null;
}
/// <summary>
/// Whether a currency may be minted by a PLAYER-facing endpoint (v2/balance, game rewards).
/// Only the three room currencies qualify. Rec Center Tokens (and every other bucket) are
/// NOT player-grantable - they can only be granted server-side / from the admin panel.
/// </summary>
public static bool IsPlayerGrantable(int currencyType)
=> currencyType == (int)CurrencyType.DraculaSilver
|| currencyType == (int)CurrencyType.LostSkullsGold
|| currencyType == (int)CurrencyType.LaserTagTickets;
/// <summary>
/// Most a single award may grant, per currency. 0 = no cap.
///
/// Crescendo pays per CHECKPOINT, and a checkpoint is worth 0..30,000 Silver -
/// five checkpoints, so 150,000 is a full game. The cap is enforced per request so
/// a bad (or forged) amount cannot mint a fortune in one call.
/// </summary>
/// <summary>
/// Whether a currency is an account balance a purchase can debit. Invalid (0) never is;
/// everything else the enum defines is a real balance bucket. Used by bulkpurchase to
/// reject a bag posted against a currency there is no wallet to spend from (400).
/// </summary>
public static bool IsSpendable(int currencyType)
=> currencyType != (int)CurrencyType.Invalid
&& Enum.IsDefined(typeof(CurrencyType), currencyType);
public static long MaxPerAward(CurrencyType currency)
{
switch (currency)
{
case CurrencyType.DraculaSilver: return 30000; // per Crescendo checkpoint
default: return 0; // uncapped
}
}
/// <summary>Clamps an award to 0..MaxPerAward for that currency.</summary>
public static long ClampAward(CurrencyType currency, long amount)
{
if (amount < 0) return 0;
long max = MaxPerAward(currency);
return (max > 0 && amount > max) ? max : amount;
}
}
}