Files
hushroom/src/features/flight-starfield/model/starfieldModel.ts
2026-02-14 21:55:54 +09:00

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;