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

65 lines
1.9 KiB
TypeScript

export const MOB_ANIMATION_NEAR_DISTANCE = 32;
export const MOB_ANIMATION_MEDIUM_DISTANCE = 62;
export const MOB_ANIMATION_MEDIUM_INTERVAL = 1 / 30;
export const MOB_ANIMATION_FAR_INTERVAL = 1 / 15;
const MAX_FRAME_DELTA = 0.05;
const CADENCE_EPSILON = 1e-6;
/** `0` means every render frame; `null` means the animation may stay frozen. */
export type MobAnimationInterval = number | null;
export interface MobAnimationCadenceState {
accumulatedDelta: number;
}
export function createMobAnimationCadenceState(): MobAnimationCadenceState {
return { accumulatedDelta: 0 };
}
/**
* Keeps combat-critical actors at full fidelity while throttling background
* presentation only. Gameplay movement and combat simulation do not use this.
*/
export function resolveMobAnimationInterval(
active: boolean,
forceFullFidelity: boolean,
distanceSquared: number,
inFrontOfCamera: boolean,
): MobAnimationInterval {
if (!active) return null;
if (forceFullFidelity) return 0;
if (distanceSquared <= MOB_ANIMATION_NEAR_DISTANCE ** 2) return 0;
if (!inFrontOfCamera) return null;
if (distanceSquared <= MOB_ANIMATION_MEDIUM_DISTANCE ** 2) {
return MOB_ANIMATION_MEDIUM_INTERVAL;
}
return MOB_ANIMATION_FAR_INTERVAL;
}
/**
* Accumulates capped render deltas and returns the elapsed time to evaluate on
* this frame. This preserves animation speed at a lower sampling frequency.
*/
export function advanceMobAnimationCadence(
state: MobAnimationCadenceState,
delta: number,
interval: MobAnimationInterval,
): number {
const safeDelta = Number.isFinite(delta)
? Math.min(Math.max(0, delta), MAX_FRAME_DELTA)
: 0;
if (interval === null) {
state.accumulatedDelta = 0;
return 0;
}
state.accumulatedDelta += safeDelta;
if (interval > 0 && state.accumulatedDelta + CADENCE_EPSILON < interval) return 0;
const updateDelta = state.accumulatedDelta;
state.accumulatedDelta = 0;
return updateDelta;
}