Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
93 lines
2.7 KiB
TypeScript
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;
|
|
}
|
|
}
|