forked from phenom/healer-man
91 lines
2.8 KiB
TypeScript
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));
|
|
}
|