import { Config } from "@systems/config"; /** * Parses a combat-stat value (ATK/DEF/Heal) that may use K/M shorthand, * case-insensitive (e.g. "500", "500k", "1.4M", "2.7m"). Comma is accepted * as a decimal separator too ("3,4M" parses the same as "3.4M") since * players naturally try it. * * A bare number with no suffix is inferred: >= 100 is assumed to already * be in thousands ("500" -> 500K), below 100 is assumed to be shorthand * millions ("1.4" -> 1.4M) — combat stats are never realistically in the * single-to-double-digit range. * * Also enforces a configurable plausibility ceiling (`Config.tg.maxStatValue`, * default 50M) — real recorded ATK/DEF values top out around 10-13M, but a * stray extra digit or a redundant M on an already-large number * ("150.0M", "124000M") silently produces a nonsense multi-billion value * with no upstream validation catching it otherwise. The cap is officer- * adjustable (`/tg-config tg set-max-stat-value`) rather than hardcoded, * since what counts as "impossible" can shift with game balance changes — * this is a sanity check, not a hard game-rule. * * Shared by the Submit Score modal, `/tg score set`, and * `/tg-admin score-inject` so all three entry points behave identically. */ export interface StatValueResult { value?: number; error?: string; } export function parseStatValue(raw: string | null | undefined, label: string): StatValueResult { if (!raw) return {}; const normalized = raw.trim().replace(",", "."); const match = normalized.match(/^(\d+(?:\.\d+)?)\s*([kKmM])?$/); if (!match) { return { error: `${label} must be ${STAT_VALUE_HINT}.` }; } const parsed = parseFloat(match[1]); const suffix = match[2]?.toLowerCase(); let value: number; if (suffix === "k") value = Math.round(parsed * 1_000); else if (suffix === "m") value = Math.round(parsed * 1_000_000); else value = Math.round(parsed >= 100 ? parsed * 1_000 : parsed * 1_000_000); const cap = Config.get({ section: "tg", key: "maxStatValue" }); if (value > cap) { return { error: `${label} of ${value.toLocaleString()} is above the ${cap.toLocaleString()} sanity limit — ` + `check for a typo (e.g. an extra digit or a redundant M/K). If this is genuinely correct, ` + `an officer can raise the limit with \`/tg-config tg set-max-stat-value\`.`, }; } return { value }; } export const STAT_VALUE_HINT = "a number, optionally with a K or M suffix (e.g. `500K`, `1.4M`)";