Release Healer Man 0.1.9

This commit is contained in:
phenom committed 2026-08-30 14:16:56 -04:00
1 parent c245a199b6
commit d060296ade
74 files changed
+5901 -334

No files matched your search

+75 -6
View File
@@ -1,12 +1,21 @@
import { useEffect, useState } from "react";
import type { AuraDefinition, AuraDisposition } from "../game/combatAuras";
import type { ActiveAura, AuraDefinition, AuraDisposition } from "../game/combatAuras";
import { useCombatStore } from "../game/combatStore";
import { formatTimedEffectTimer } from "../game/timedEffectDisplay";
import type { StatusEffectInspectHandler, StatusEffectInspection } from "./statusEffectInspection";
export const LONG_TERM_AURA_DURATION_MS = 60_000;
interface AuraStripProps {
readonly targetId: string;
readonly targetIds?: readonly string[];
readonly dispositions?: readonly AuraDisposition[];
readonly className?: string;
readonly hideLongTerm?: boolean;
readonly showOnlyLongTerm?: boolean;
readonly targetLabels?: Readonly<Record<string, string>>;
readonly onInspect?: StatusEffectInspectHandler;
readonly tooltipId?: string;
}
export function auraDefinitionDisplaysInStrip(definition: AuraDefinition): boolean {
@@ -17,17 +26,55 @@ export function auraDefinitionIcon(definition: AuraDefinition): string | null {
return definition.icon?.trim() || null;
}
export function auraDisplayName(definition: AuraDefinition): string {
return definition.name.replace(/\s+\((?:DBC\s+\d+,\s*)?Aura\s+\d+\)$/i, "").trim();
}
/** Permanent and one-minute-or-longer auras are persistent party-frame clutter. */
export function auraDefinitionIsLongTerm(definition: AuraDefinition): boolean {
return definition.durationMs === null || definition.durationMs >= LONG_TERM_AURA_DURATION_MS;
}
export function auraDescription(aura: ActiveAura): string {
const authored = aura.definition.description?.trim();
if (authored) return authored;
const absorb = aura.absorbs.reduce((total, pool) => total + pool.remaining, 0);
if (absorb > 0) return `Absorbs up to ${Math.ceil(absorb)} incoming damage.`;
if (aura.definition.controlImmunities?.length) {
return `Grants immunity to ${aura.definition.controlImmunities.join(", ")}.`;
}
if (aura.definition.modifiers?.length) {
return aura.definition.disposition === "buff"
? "Improves combat attributes while active."
: "Reduces combat effectiveness while active.";
}
if (aura.definition.procs?.length) return "Can trigger an additional effect during combat.";
return aura.definition.disposition === "buff"
? "A beneficial effect is active."
: "A harmful effect is active.";
}
export function AuraStrip({
targetId,
targetIds,
dispositions = ["buff", "debuff"],
className = "",
hideLongTerm = false,
showOnlyLongTerm = false,
targetLabels,
onInspect,
tooltipId,
}: AuraStripProps) {
const allAuras = useCombatStore((state) => state.auras);
const [now, setNow] = useState(() => Date.now());
const requestedTargetIds = targetIds ?? [targetId];
const auras = allAuras.filter((aura) => (
aura.targetId === targetId
requestedTargetIds.includes(aura.targetId)
&& dispositions.includes(aura.definition.disposition)
&& auraDefinitionDisplaysInStrip(aura.definition)
&& (!hideLongTerm || !auraDefinitionIsLongTerm(aura.definition))
&& (!showOnlyLongTerm || auraDefinitionIsLongTerm(aura.definition))
&& (aura.expiresAt === null || aura.expiresAt > now)
));
@@ -44,14 +91,36 @@ export function AuraStrip({
{auras.map((aura) => {
const remaining = aura.expiresAt === null ? null : Math.max(0, aura.expiresAt - now);
const timer = remaining === null ? "" : formatTimedEffectTimer(remaining);
const initial = aura.definition.name.trim().charAt(0).toUpperCase() || "*";
const displayName = auraDisplayName(aura.definition);
const targetLabel = targetLabels?.[aura.targetId];
const initial = displayName.charAt(0).toUpperCase() || "*";
const icon = auraDefinitionIcon(aura.definition);
const inspection: StatusEffectInspection = {
id: aura.instanceId,
name: displayName,
disposition: aura.definition.disposition,
description: auraDescription(aura),
remainingLabel: timer ? `${timer} remaining` : "Permanent",
stacks: aura.stacks,
...(targetLabel ? { targetLabel } : {}),
...(aura.definition.dispelCategory
? { dispelCategory: aura.definition.dispelCategory }
: {}),
};
return (
<span
key={aura.instanceId}
className={`aura aura--${aura.definition.disposition}`}
aria-label={`${aura.definition.name}, ${aura.definition.disposition}${timer ? `, ${timer} remaining` : ""}`}
title={`${aura.definition.name}${timer ? ` · ${timer} remaining` : ""}`}
className={`aura aura--${aura.definition.disposition} ${onInspect ? "aura--inspectable" : ""}`.trim()}
tabIndex={onInspect ? 0 : undefined}
aria-label={`${displayName}${targetLabel ? ` on ${targetLabel}` : ""}, ${aura.definition.disposition}${timer ? `, ${timer} remaining` : ""}`}
aria-describedby={onInspect ? tooltipId : undefined}
title={onInspect ? undefined : `${displayName}${timer ? ` · ${timer} remaining` : ""}`}
onPointerEnter={onInspect ? () => onInspect(inspection) : undefined}
onPointerLeave={onInspect ? (event) => {
if (event.currentTarget !== document.activeElement) onInspect(null);
} : undefined}
onFocus={onInspect ? () => onInspect(inspection) : undefined}
onBlur={onInspect ? () => onInspect(null) : undefined}
>
{icon
? <img className="aura__icon" src={icon} alt="" draggable={false} />