Files
healer-man/src/game/hudLayout.ts
T
2026-08-14 15:56:39 -04:00

62 lines
1.8 KiB
TypeScript

export interface NormalizedHudPosition {
readonly x: number;
readonly y: number;
}
export interface HudSize {
readonly width: number;
readonly height: number;
}
export const DEFAULT_THREAT_METER_POSITION: NormalizedHudPosition = Object.freeze({
x: 0.78,
y: 0.18,
});
function finite(value: number, fallback: number): number {
return Number.isFinite(value) ? value : fallback;
}
function clamp(value: number, minimum: number, maximum: number): number {
return Math.max(minimum, Math.min(maximum, value));
}
export function normalizeHudPosition(
value: unknown,
fallback: NormalizedHudPosition = DEFAULT_THREAT_METER_POSITION,
): NormalizedHudPosition {
if (!value || typeof value !== "object") return fallback;
const candidate = value as Partial<NormalizedHudPosition>;
return {
x: clamp(finite(candidate.x ?? Number.NaN, fallback.x), 0, 1),
y: clamp(finite(candidate.y ?? Number.NaN, fallback.y), 0, 1),
};
}
/**
* Keeps a center-anchored HUD panel inside the visible viewport. Positions are
* normalized so a saved desktop location remains meaningful on smaller screens.
*/
export function clampHudPositionToViewport(
position: NormalizedHudPosition,
viewport: HudSize,
panel: HudSize,
padding = 6,
): NormalizedHudPosition {
const viewportWidth = Math.max(1, finite(viewport.width, 1));
const viewportHeight = Math.max(1, finite(viewport.height, 1));
const horizontalInset = Math.min(
0.5,
(Math.max(0, finite(panel.width, 0)) / 2 + Math.max(0, padding)) / viewportWidth,
);
const verticalInset = Math.min(
0.5,
(Math.max(0, finite(panel.height, 0)) / 2 + Math.max(0, padding)) / viewportHeight,
);
const normalized = normalizeHudPosition(position);
return {
x: clamp(normalized.x, horizontalInset, 1 - horizontalInset),
y: clamp(normalized.y, verticalInset, 1 - verticalInset),
};
}