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

55 lines
1.8 KiB
TypeScript

import type { AggroActorId, AggroRole, ForcedTarget, ThreatTable } from "./aggro";
export interface ThreatMeterActor {
readonly id: AggroActorId;
readonly name: string;
readonly role: AggroRole;
readonly alive: boolean;
}
export interface ThreatMeterRow {
readonly actorId: AggroActorId;
readonly name: string;
readonly role: AggroRole;
readonly threat: number;
readonly percentOfLeader: number;
readonly currentTarget: boolean;
readonly forced: boolean;
}
export function threatMeterRows(
table: ThreatTable,
actors: readonly ThreatMeterActor[],
currentTargetActorId: AggroActorId | null,
forcedTarget: ForcedTarget | null,
now: number,
maximumRows = 5,
): readonly ThreatMeterRow[] {
const actorById = new Map(actors.filter((actor) => actor.alive).map((actor) => [actor.id, actor]));
const ranked = Object.entries(table)
.map(([actorId, threat]) => ({
actor: actorById.get(actorId),
threat: Number.isFinite(threat) ? Math.max(0, threat) : 0,
}))
.filter((entry): entry is { actor: ThreatMeterActor; threat: number } => (
Boolean(entry.actor) && entry.threat > 0
))
.sort((left, right) => (
right.threat - left.threat
|| Number(right.actor.id === currentTargetActorId) - Number(left.actor.id === currentTargetActorId)
|| left.actor.id.localeCompare(right.actor.id)
))
.slice(0, Math.max(0, Math.trunc(maximumRows)));
const leaderThreat = ranked[0]?.threat ?? 0;
const forcedActorId = forcedTarget && forcedTarget.endsAt > now ? forcedTarget.actorId : null;
return ranked.map(({ actor, threat }) => ({
actorId: actor.id,
name: actor.name,
role: actor.role,
threat,
percentOfLeader: leaderThreat > 0 ? threat / leaderThreat * 100 : 0,
currentTarget: actor.id === currentTargetActorId,
forced: actor.id === forcedActorId,
}));
}