Release Healer Man 0.1.28

This commit is contained in:
phenom committed 2026-09-10 14:14:02 -04:00
1 parent ed8b9e3719
commit 8c9809d3e0
23 files changed
+975 -55

No files matched your search

+45 -32
View File
@@ -32,6 +32,9 @@ import { AuraStrip } from "./AuraStrip";
import { ManastormStatus } from "./ManastormStatus";
import { ThreatMeterWidget } from "./ThreatMeterWidget";
import { BossLootWindow } from "./BossLootWindow";
import { EditableHudElement } from "./EditableHudElement";
import { DesktopActionBars } from "./DesktopActionBars";
import { currentUiDevice, useUiLayoutStore } from "../game/uiLayoutStore";
import { SummonFrames } from "./SummonFrames";
import { useOnlineSessionStore } from "../app/onlineSessionStore";
import { onlineAccountIdFromActor, onlinePlayerActorId } from "../game/onlineSessionRuntime";
@@ -139,6 +142,8 @@ export function DungeonCompletePrompt({
}
export function Hud() {
const ui = useUiLayoutStore();
const desktopBars = currentUiDevice() === "pc" && (ui.draft ?? ui.preferences.current.pc).wowBars;
const character = useShellStore((state) => state.activeCharacter);
const gameMode = useGameStore((state) => state.gameMode);
const areaName = useGameStore((state) => state.areaName);
@@ -216,10 +221,12 @@ export function Hud() {
const classColor = classById(classId).color;
const resourceTone = resourceProfileForClass(classId).type;
const actionItems = useMemo<ActionBarItems>(() => Object.fromEntries(ACTION_CONTROLS.map((control) => {
const abilityId = actionBindings[actionBindingId(actionLayer, control)];
const catalogAbility = abilityId ? abilities.find((candidate) => candidate.id === abilityId) ?? null : null;
if (!catalogAbility) return [control, null];
const assignedAbilities = useMemo(() => {
const used = new Set(Object.values(actionBindings));
if (desktopBars) for (const bar of (ui.draft ?? ui.preferences.current.pc).bars) for (const slot of bar.slots) used.add(slot.abilityId);
return abilities.filter((ability) => used.has(ability.id));
}, [abilities, actionBindings, desktopBars, ui.draft, ui.preferences]);
const abilityItems = useMemo<Record<string, ActionBarItem>>(() => Object.fromEntries(assignedAbilities.map((catalogAbility) => {
const learnedTalentRank = catalogAbility.talentEntryId === undefined
? undefined
: talentRanks[`coa-entry-${catalogAbility.talentEntryId}`] ?? 0;
@@ -254,8 +261,11 @@ export function Hud() {
lockedUntilLevel: level < ability.unlockLevel ? ability.unlockLevel : undefined,
target: ability.target,
};
return [control, item];
})) as ActionBarItems, [abilities, actionBindings, actionLayer, activeCast, auras, classColor, classId, clock, cooldowns, globalCooldownEndsAt, health, level, maxHealth, maxResource, maxResourcePools, resource, resourceName, resourcePools, resourceTone, talentModifiers, talentRanks]);
return [catalogAbility.id, item];
})), [assignedAbilities, activeCast, auras, classColor, classId, clock, cooldowns, globalCooldownEndsAt, health, level, maxHealth, maxResource, maxResourcePools, resource, resourceName, resourcePools, resourceTone, talentModifiers, talentRanks]);
const actionItems = Object.fromEntries(ACTION_CONTROLS.map((control) => [control,
abilityItems[actionBindings[actionBindingId(actionLayer, control)] ?? ""] ?? null,
])) as ActionBarItems;
const castBinding = (control: (typeof ACTION_CONTROLS)[number]) => useCombatStore.getState().castActionBinding(
useGameStore.getState().actionLayer,
@@ -362,17 +372,17 @@ export function Hud() {
<div className="hud" style={{ "--game-ui-scale": settings.uiScale } as CSSProperties}>
<ManastormStatus />
<BossLootWindow />
{dungeonComplete && health > 0 ? (
<EditableHudElement id="completion">{dungeonComplete && health > 0 ? (
<DungeonCompletePrompt dungeonTitle={dungeon.title} onFinish={finishDungeon} />
) : null}
{feedback?.kind === "loot" ? (
) : null}</EditableHudElement>
<EditableHudElement id="loot">{feedback?.kind === "loot" ? (
<section className="loot-reward" role="status" aria-live="polite">
<span aria-hidden="true">◆</span>
<p><strong>Loot acquired</strong><small>{feedback.message}</small><em>Press I to inspect and equip</em></p>
</section>
) : null}
<PartyFrames />
<SummonFrames now={clock} />
) : null}</EditableHudElement>
<EditableHudElement id="party"><PartyFrames /></EditableHudElement>
<EditableHudElement id="summons"><SummonFrames now={clock} /></EditableHudElement>
{pendingGroundTarget ? (
<section className="ground-target-reticle" role="status" aria-live="polite">
@@ -391,19 +401,19 @@ export function Hud() {
) : null}
<div className="hud-top-right">
<Minimap
<EditableHudElement id="minimap"><Minimap
position={playerPosition}
yaw={cameraYaw}
areaName={stagePresentation.areaName}
distanceTravelled={distance}
rotationMode={settings.minimapRotation}
onOpenMap={toggleMap}
/>
<PlayerStatusPanel now={clock} />
<EncounterTimeline now={clock} />
/></EditableHudElement>
<EditableHudElement id="player"><PlayerStatusPanel now={clock} /></EditableHudElement>
<EditableHudElement id="timeline"><EncounterTimeline now={clock} /></EditableHudElement>
</div>
{target && settings.showTargetFrame && (
<EditableHudElement id="target">{target && settings.showTargetFrame && (
<section className={`target-frame ${target.boss ? "target-frame--boss" : ""}`} aria-label={`Target ${target.name}`}>
<header><strong>{target.name}</strong><span>{target.boss ? "Boss" : `Level ${target.level}`}</span></header>
<div className="target-frame__track"><span style={{ width: `${targetPercent}%` }} /></div>
@@ -420,18 +430,18 @@ export function Hud() {
<AuraStrip targetId={target.id} className="aura-strip--target" />
<small>{Math.ceil(target.health)} / {target.maxHealth}</small>
</section>
)}
)}</EditableHudElement>
{target && settings.showThreatMeter && threatRows.length > 0 && (
<EditableHudElement id="threat">{target && settings.showThreatMeter && threatRows.length > 0 && (
<ThreatMeterWidget
targetName={target.name}
rows={threatRows}
position={settings.threatMeterPosition}
onPositionChange={updateThreatMeterPosition}
/>
)}
)}</EditableHudElement>
{selectedFriendly && settings.showTargetFrame && (
<EditableHudElement id="friendly">{selectedFriendly && settings.showTargetFrame && (
<section className="target-frame target-frame--friendly" aria-label={`Friendly target ${selectedFriendly.name}${selectedFriendly.self ? ", self" : ""}`}>
<header>
<strong>{selectedFriendly.name}</strong>
@@ -448,10 +458,10 @@ export function Hud() {
/>
<small>{Math.ceil(selectedFriendly.health)} / {selectedFriendly.maxHealth}</small>
</section>
)}
)}</EditableHudElement>
<div className="hud-bottom-stack">
{activeCast && castingAbility && (
<EditableHudElement id="cast">{activeCast && castingAbility && (
<section className={`cast-bar cast-bar--${activeCast.mode}`} aria-label={`${activeCast.mode === "channel" ? "Channeling" : "Casting"} ${castingAbility.name}`}>
<header><strong>{castingAbility.name}</strong><span>{castRemaining.toFixed(1)}s</span></header>
<div className="cast-bar__track">
@@ -459,9 +469,9 @@ export function Hud() {
</div>
<small>{activeCast.mode === "channel" ? "Channeling" : "Casting"}</small>
</section>
)}
)}</EditableHudElement>
{!activeCast && (
<EditableHudElement id="hints">{!activeCast && (
<div className={`control-hint ${hasMoved && cameraLookActive ? "control-hint--settled" : ""}`}>
{inputMode === "gamepad"
? "Left stick move | L1 / L2 layers | Face / R1 / R2 / D-pad abilities | R3 target | Start pause"
@@ -469,21 +479,24 @@ export function Hud() {
? "Release right mouse for cursor | WASD move | Arrow keys look | 1-8 abilities | Shift / Alt layers"
: "Right mouse / Arrow keys look | Click party frames to target | WASD move | Tab enemy"}
</div>
)}
)}</EditableHudElement>
<ActionBar
{!desktopBars && <EditableHudElement id="actions"><ActionBar
items={actionItems}
activeLayer={actionLayer}
onActivate={castBinding}
inputMode={inputMode}
feedback={settings.showFloatingCombatText ? feedback?.message ?? null : null}
/>
/></EditableHudElement>}
</div>
<ExperienceBar level={level} current={xp} required={xpToNext} />
<DesktopActionBars items={abilityItems} />
<EditableHudElement id="feedback">{desktopBars && settings.showFloatingCombatText && feedback?.message
? <div className="desktop-action-feedback" role="status">{feedback.message}</div> : null}</EditableHudElement>
<EditableHudElement id="experience"><ExperienceBar level={level} current={xp} required={xpToNext} /></EditableHudElement>
{cameraLookActive && <span className="reticle" aria-hidden="true" />}
<EditableHudElement id="reticle">{cameraLookActive && <span className="reticle" aria-hidden="true" />}</EditableHudElement>
{assetStatus === "proxy" && (
<EditableHudElement id="assets">{assetStatus === "proxy" && (
<div className="asset-notice" role="status">
<strong>Cave proxy active</strong>
<span>{assetMessage ?? `Add ${[
@@ -491,7 +504,7 @@ export function Hud() {
...dungeon.assets.collision,
].map((asset) => asset.fileName).join(" and ")}.`}</span>
</div>
)}
)}</EditableHudElement>
<LoadingOverlay />
{health <= 0 ? (
<DefeatedPrompt