import { FLIGHT_STARFIELD_TUNING } from "@/shared/config/starfield"; import { clamp, randomInRange } from "@/shared/lib/math/number"; import { FlightStar } from "@/features/flight-starfield/model/types"; export const getFlightStarCount = (width: number, height: number) => { const isMobile = width < FLIGHT_STARFIELD_TUNING.mobileBreakpoint; const min = isMobile ? FLIGHT_STARFIELD_TUNING.starCount.mobile.min : FLIGHT_STARFIELD_TUNING.starCount.desktop.min; const max = isMobile ? FLIGHT_STARFIELD_TUNING.starCount.mobile.max : FLIGHT_STARFIELD_TUNING.starCount.desktop.max; const byArea = Math.round( (width * height) / FLIGHT_STARFIELD_TUNING.densityDivisor, ); return clamp(byArea, min, max); }; export const createFlightVanishXJitter = () => { const sign = Math.random() < 0.5 ? -1 : 1; return ( sign * randomInRange( FLIGHT_STARFIELD_TUNING.vanishXJitter.min, FLIGHT_STARFIELD_TUNING.vanishXJitter.max, ) ); }; const createFlightSpeed = () => { const tier = Math.random(); if (tier < FLIGHT_STARFIELD_TUNING.speedTiers.slow.chance) { return randomInRange( FLIGHT_STARFIELD_TUNING.speedTiers.slow.min, FLIGHT_STARFIELD_TUNING.speedTiers.slow.max, ); } if (tier < FLIGHT_STARFIELD_TUNING.speedTiers.medium.chance) { return randomInRange( FLIGHT_STARFIELD_TUNING.speedTiers.medium.min, FLIGHT_STARFIELD_TUNING.speedTiers.medium.max, ); } return randomInRange( FLIGHT_STARFIELD_TUNING.speedTiers.fast.min, FLIGHT_STARFIELD_TUNING.speedTiers.fast.max, ); }; const createFlightVisualTier = () => { const highlight = Math.random() < FLIGHT_STARFIELD_TUNING.radius.highlightChance; const tailRoll = Math.random(); const tailLength = tailRoll < FLIGHT_STARFIELD_TUNING.tail.pointChance ? randomInRange( FLIGHT_STARFIELD_TUNING.tail.pointRange.min, FLIGHT_STARFIELD_TUNING.tail.pointRange.max, ) : tailRoll < FLIGHT_STARFIELD_TUNING.tail.shortChance ? randomInRange( FLIGHT_STARFIELD_TUNING.tail.shortRange.min, FLIGHT_STARFIELD_TUNING.tail.shortRange.max, ) : randomInRange( FLIGHT_STARFIELD_TUNING.tail.longRange.min, FLIGHT_STARFIELD_TUNING.tail.longRange.max, ); return { radius: highlight ? randomInRange( FLIGHT_STARFIELD_TUNING.radius.highlight.min, FLIGHT_STARFIELD_TUNING.radius.highlight.max, ) : randomInRange( FLIGHT_STARFIELD_TUNING.radius.normal.min, FLIGHT_STARFIELD_TUNING.radius.normal.max, ), alpha: highlight ? randomInRange( FLIGHT_STARFIELD_TUNING.alpha.highlight.min, FLIGHT_STARFIELD_TUNING.alpha.highlight.max, ) : randomInRange( FLIGHT_STARFIELD_TUNING.alpha.normal.min, FLIGHT_STARFIELD_TUNING.alpha.normal.max, ), tailLength, }; }; const createFlightSpawnRadius = (width: number, height: number) => { const roll = Math.random(); const maxWideRadius = Math.min( Math.max(width, height) * FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.maxScaleOfViewport, FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.maxAbsolute, ); const ringOuter = Math.min( FLIGHT_STARFIELD_TUNING.spawnRadius.ringRange.max, maxWideRadius, ); if (roll < FLIGHT_STARFIELD_TUNING.spawnRadius.centerChance) { return randomInRange( FLIGHT_STARFIELD_TUNING.spawnRadius.centerRange.min, FLIGHT_STARFIELD_TUNING.spawnRadius.centerRange.max, ); } if (roll < FLIGHT_STARFIELD_TUNING.spawnRadius.ringChance) { return randomInRange( FLIGHT_STARFIELD_TUNING.spawnRadius.ringRange.min, Math.max(FLIGHT_STARFIELD_TUNING.spawnRadius.ringRange.min, ringOuter), ); } return randomInRange( FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.min, Math.max(FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.min, maxWideRadius), ); }; export const createFlightStar = ({ width, height, isRespawn, }: { width: number; height: number; isRespawn: boolean; }): FlightStar => { const radius = createFlightSpawnRadius(width, height); const angle = randomInRange(0, Math.PI * 2); const visuals = createFlightVisualTier(); return { wx: Math.cos(angle) * radius, wy: Math.sin(angle) * radius, z: isRespawn ? randomInRange( FLIGHT_STARFIELD_TUNING.zRange.respawn.min, FLIGHT_STARFIELD_TUNING.zRange.respawn.max, ) : randomInRange( FLIGHT_STARFIELD_TUNING.zRange.initial.min, FLIGHT_STARFIELD_TUNING.zRange.initial.max, ), speed: createFlightSpeed(), radius: visuals.radius, alpha: visuals.alpha, tailLength: visuals.tailLength, }; }; export const shouldRecycleFlightStar = ({ x, y, z, width, height, }: { x: number; y: number; z: number; width: number; height: number; }) => z <= FLIGHT_STARFIELD_TUNING.zRange.recycleThreshold || x < -50 || x > width + 50 || y < -50 || y > height + 50;