180 lines
5.0 KiB
TypeScript
180 lines
5.0 KiB
TypeScript
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;
|