1
0
Fork 0
polaris-task-force/src/components/frontend/minigames/qualification/crosshair.ts
2026-09-11 03:25:21 -04:00

93 lines
2.7 KiB
TypeScript

export type AimTrainerCrosshairStyle = "classic" | "dot" | "cross" | "circle" | "chevron";
export type AimTrainerCrosshairPrefs = {
style: AimTrainerCrosshairStyle;
color: string;
scale: number;
};
export const AIM_TRAINER_CROSSHAIR_STORAGE_KEY = "ptf.aimTrainer.crosshair";
export const AIM_TRAINER_CROSSHAIR_SCALE_MIN = 1;
export const AIM_TRAINER_CROSSHAIR_SCALE_MAX = 2.5;
export const AIM_TRAINER_CROSSHAIR_SCALE_STEP = 0.1;
export const AIM_TRAINER_CROSSHAIR_STYLES = [
"classic",
"dot",
"cross",
"circle",
"chevron",
] as const satisfies readonly AimTrainerCrosshairStyle[];
export const DEFAULT_AIM_TRAINER_CROSSHAIR_PREFS: AimTrainerCrosshairPrefs = {
style: "classic",
color: "#d8d3b6",
scale: AIM_TRAINER_CROSSHAIR_SCALE_MIN,
};
function createDefaultAimTrainerCrosshairPrefs(): AimTrainerCrosshairPrefs {
return { ...DEFAULT_AIM_TRAINER_CROSSHAIR_PREFS };
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isAimTrainerCrosshairStyle(value: unknown): value is AimTrainerCrosshairStyle {
return (
typeof value === "string" &&
AIM_TRAINER_CROSSHAIR_STYLES.some((style) => style === value)
);
}
function isHexColor(value: unknown): value is string {
return typeof value === "string" && /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(value);
}
export function normalizeAimTrainerCrosshairScale(value: unknown): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
return AIM_TRAINER_CROSSHAIR_SCALE_MIN;
}
const clamped = Math.min(
AIM_TRAINER_CROSSHAIR_SCALE_MAX,
Math.max(AIM_TRAINER_CROSSHAIR_SCALE_MIN, value),
);
return Number(
(Math.round(clamped / AIM_TRAINER_CROSSHAIR_SCALE_STEP) * AIM_TRAINER_CROSSHAIR_SCALE_STEP).toFixed(
1,
),
);
}
export function loadAimTrainerCrosshairPrefs(): AimTrainerCrosshairPrefs {
const defaults = createDefaultAimTrainerCrosshairPrefs();
if (typeof window === "undefined") return defaults;
try {
const stored = window.localStorage.getItem(AIM_TRAINER_CROSSHAIR_STORAGE_KEY);
if (!stored) return defaults;
const parsed: unknown = JSON.parse(stored);
if (!isRecord(parsed) || !isAimTrainerCrosshairStyle(parsed.style)) return defaults;
if (!isHexColor(parsed.color)) return defaults;
return {
style: parsed.style,
color: parsed.color,
scale: normalizeAimTrainerCrosshairScale(parsed.scale),
};
} catch {
return defaults;
}
}
export function saveAimTrainerCrosshairPrefs(prefs: AimTrainerCrosshairPrefs): void {
if (typeof window === "undefined") return;
try {
window.localStorage.setItem(AIM_TRAINER_CROSSHAIR_STORAGE_KEY, JSON.stringify(prefs));
} catch {
return;
}
}