// ═══════════════════════════════════════════════════════════════ // Gameplay constants — all tunable magic numbers in one place // ═══════════════════════════════════════════════════════════════ // --- Performance mode --- const PERF_KEY = 'bf_perf_mode' function detectLowEnd(): boolean { if (typeof navigator === 'undefined') return false const cores = navigator.hardwareConcurrency || 4 const mobile = navigator.maxTouchPoints > 0 return cores < 4 || mobile } function loadPerfPref(): boolean | null { try { const v = localStorage.getItem(PERF_KEY) if (v === '1') return true if (v === '0') return false } catch {} return null } let _perfMode = loadPerfPref() ?? detectLowEnd() export function isPerfMode(): boolean { return _perfMode } export function setPerfMode(on: boolean): void { _perfMode = on try { localStorage.setItem(PERF_KEY, on ? '1' : '0') } catch {} } /** Scale a particle count down in performance mode (40%) or reduced motion (20%) */ export function perfParticles(count: number): number { if (_reducedMotion) return Math.max(1, Math.round(count * 0.2)) return _perfMode ? Math.max(1, Math.round(count * 0.4)) : count } // --- Reduced motion --- const _reducedMotion = typeof window !== 'undefined' ? window.matchMedia('(prefers-reduced-motion: reduce)').matches : false export function isReducedMotion(): boolean { return _reducedMotion } // --- Haptic feedback --- export function haptic(ms: number = 50): void { try { navigator?.vibrate?.(ms) } catch { /* no vibration support */ } } // --- Choreography selection probabilities --- export const CREATOR_ULTIMATE_CHANCE = 0.5 // Creator ultimate chance (non-crit); 1.0 on crits export const CREATOR_THEMED_MOVE_RATIO = 0.6 // Creator uses creator-themed moves 60% of the time export const CRIT_THEMED_RATIO = 0.5 // On crits: 50% themed, 50% epic generic export const CHOREOGRAPHY_THEMED_RATIO = 0.6 // Normal: 60% themed export const CHOREOGRAPHY_GENERIC_CUTOFF = 0.85 // Normal: 25% generic (0.60–0.85), 15% wild (0.85–1.0) // --- Tier-gated ultimate chances --- export const TIER_ULTIMATE_CHANCES: Record = { 2: 0.15, 3: 0.20, 4: 0.30, 5: 0.40, } // --- Morph system --- export const CREATOR_MORPH_CRIT_CHANCE = 0.7 // Creator morphs on devastating crits 70% export const CREATOR_MORPH_INTENSITY_THRESHOLD = 0.6 // Creator morphs when intensity > 0.6 export const BOT_MORPH_CRIT_CHANCE = 0.4 // Regular bots morph on devastating crits 40% export const BOT_MORPH_INTENSITY_THRESHOLD = 0.7 // Regular bots morph when intensity > 0.7 // --- Creator cameo --- export const CREATOR_CAMEO_CHANCE = 0.06 // 6% per round in non-creator fights // --- Round exchange probabilities --- export const DODGE_CHANCE = 0.12 // Defender dodges instead of being hit export const COUNTER_ATTACK_CHANCE = 0.25 // Defender counters after getting hit export const SCANLINE_GLITCH_CHANCE = 0.25 // Random scanline glitch during exchanges export const COMEDY_SFX_CHANCE = 0.15 // Comedy sound on non-decisive hits export const PHYSICAL_CONTACT_BASE = 0.35 // Base chance for brawl/clinch export const PHYSICAL_CONTACT_INTENSITY_SCALE = 0.2 // Added per unit of intensity export const PHYSICAL_CONTACT_EXCHANGE_BONUS = 0.15 // Added after first exchange export const CROWD_REACTION_CHANCE = 0.4 // Crowd reacts to round results export const MAYBE_SHOW_HUMAN_CHANCE = 0.2 // Show human coach export const CREATOR_VOICE_LINE_CHANCE = 0.6 // Creator gets a voice line // --- Hyperdetail (grotesque close-up) --- export const HYPERDETAIL_BASE_CHANCE = 0.05 // Base hyperdetail chance export const HYPERDETAIL_INTENSITY_SCALE = 0.45 // Scales with intensity