export interface LevelBenchmark { level: number; xpToNextLevel: number; cumulativeXpRequired: number; referenceStrength: number; baseHp: number; baseMana: number; baselineDamageScale: number; } export const MAX_PLAYER_LEVEL = 20; function clampLevel(value: unknown) { if (typeof value !== 'number' || !Number.isFinite(value)) { return 1; } return Math.min(MAX_PLAYER_LEVEL, Math.max(1, Math.floor(value))); } function roundMetric(value: number, digits = 3) { return Number(value.toFixed(digits)); } function computeXpToNextLevel(level: number) { const scale = Math.max(0, level - 1); return 60 + 20 * scale + 8 * scale * scale; } function buildLevelBenchmarks(maxLevel: number) { const benchmarks: LevelBenchmark[] = []; let cumulativeXpRequired = 0; for (let level = 1; level <= maxLevel; level += 1) { const scale = level - 1; const xpToNextLevel = level >= maxLevel ? 0 : computeXpToNextLevel(level); benchmarks.push({ level, xpToNextLevel, cumulativeXpRequired, referenceStrength: 100 + 16 * scale + 6 * scale * scale, baseHp: 180 + 24 * scale + 10 * scale * scale, baseMana: 80 + 14 * scale + 6 * scale * scale, baselineDamageScale: roundMetric(1 + 0.12 * scale + 0.03 * scale * scale), }); cumulativeXpRequired += xpToNextLevel; } return benchmarks; } const LEVEL_BENCHMARKS = buildLevelBenchmarks(MAX_PLAYER_LEVEL); const LEVEL_BENCHMARKS_BY_LEVEL = new Map( LEVEL_BENCHMARKS.map((benchmark) => [benchmark.level, benchmark]), ); export function getLevelBenchmark(level: number) { return ( LEVEL_BENCHMARKS_BY_LEVEL.get(clampLevel(level)) ?? LEVEL_BENCHMARKS[0]! ); }