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

91 lines
2.8 KiB
TypeScript

export const MANASTORM_BUBBLE_DEFAULT_RADIUS = 9;
export const MANASTORM_BUBBLE_DANGER_DISTANCE = 1.5;
export const MANASTORM_BUBBLE_WARNING_DISTANCE = 3;
export type ManastormBubbleSeverity = "safe" | "warning" | "danger";
export interface ManastormSafetyBubbleState {
readonly active: boolean;
readonly radius: number;
readonly distanceRemaining: number;
readonly severity: ManastormBubbleSeverity;
}
export interface ManastormClientOverlayState {
readonly nextWaveCountdownSeconds: number | null;
readonly safetyBubble: ManastormSafetyBubbleState;
}
export interface ManastormSafetyBubbleUpdate {
readonly active: boolean;
readonly radius?: number;
readonly distanceRemaining?: number;
}
function finiteOr(value: number | undefined, fallback: number): number {
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
}
export function manastormBubbleSeverity(distanceRemaining: number): ManastormBubbleSeverity {
if (distanceRemaining <= MANASTORM_BUBBLE_DANGER_DISTANCE) return "danger";
if (distanceRemaining <= MANASTORM_BUBBLE_WARNING_DISTANCE) return "warning";
return "safe";
}
export function createManastormClientOverlayState(): ManastormClientOverlayState {
return {
nextWaveCountdownSeconds: null,
safetyBubble: {
active: false,
radius: MANASTORM_BUBBLE_DEFAULT_RADIUS,
distanceRemaining: MANASTORM_BUBBLE_DEFAULT_RADIUS,
severity: "safe",
},
};
}
export function withManastormNextWaveCountdown(
state: ManastormClientOverlayState,
seconds: number | null,
): ManastormClientOverlayState {
const normalized = typeof seconds === "number" && Number.isFinite(seconds) && seconds >= 0
? Math.floor(seconds)
: null;
if (state.nextWaveCountdownSeconds === normalized) return state;
return { ...state, nextWaveCountdownSeconds: normalized };
}
export function withManastormSafetyBubble(
state: ManastormClientOverlayState,
update: ManastormSafetyBubbleUpdate,
): ManastormClientOverlayState {
if (!update.active) {
const initial = createManastormClientOverlayState().safetyBubble;
if (!state.safetyBubble.active) return state;
return { ...state, safetyBubble: initial };
}
const radius = Math.max(
Number.EPSILON,
finiteOr(update.radius, MANASTORM_BUBBLE_DEFAULT_RADIUS),
);
const distanceRemaining = Math.max(
0,
Math.min(radius, finiteOr(update.distanceRemaining, radius)),
);
return {
...state,
safetyBubble: {
active: true,
radius,
distanceRemaining,
severity: manastormBubbleSeverity(distanceRemaining),
},
};
}
export function manastormBubbleFillPercent(bubble: ManastormSafetyBubbleState): number {
if (!bubble.active || bubble.radius <= 0) return 0;
return Math.max(0, Math.min(100, bubble.distanceRemaining / bubble.radius * 100));
}