Release Healer Man 0.1.6
This commit is contained in:
1 parent
55cfd43d66
commit
b12fb84859
96 files changed
+10874
-609
No files matched your search
+70
-5
@@ -24,6 +24,9 @@ import { AuraStrip } from "./AuraStrip";
|
||||
import { ManastormStatus } from "./ManastormStatus";
|
||||
import { ThreatMeterWidget } from "./ThreatMeterWidget";
|
||||
import { BossLootWindow } from "./BossLootWindow";
|
||||
import { SummonFrames } from "./SummonFrames";
|
||||
import { useOnlineSessionStore } from "../app/onlineSessionStore";
|
||||
import { onlineAccountIdFromActor, onlinePlayerActorId } from "../game/onlineSessionRuntime";
|
||||
|
||||
function LoadingOverlay() {
|
||||
const activeDungeonId = useGameStore((state) => state.activeDungeonId);
|
||||
@@ -61,12 +64,14 @@ export function DefeatedPrompt({
|
||||
gameMode,
|
||||
resurrectionCharges,
|
||||
onReturn,
|
||||
sharedOnline = false,
|
||||
}: {
|
||||
gameMode: "dungeon" | "manastorm";
|
||||
resurrectionCharges: number;
|
||||
onReturn: () => void;
|
||||
sharedOnline?: boolean;
|
||||
}) {
|
||||
const canReturn = gameMode === "dungeon" || resurrectionCharges > 0;
|
||||
const canReturn = !sharedOnline && (gameMode === "dungeon" || resurrectionCharges > 0);
|
||||
return (
|
||||
<section
|
||||
className="defeated-prompt"
|
||||
@@ -86,10 +91,12 @@ export function DefeatedPrompt({
|
||||
? `Resurrect now · ${resurrectionCharges} remaining`
|
||||
: "Respawn at entrance"}
|
||||
</button>
|
||||
) : sharedOnline ? (
|
||||
<small>Your shared run is still active. A living player can resurrect you.</small>
|
||||
) : (
|
||||
<small>No shared resurrection charges remain. A living ally must resurrect you.</small>
|
||||
)}
|
||||
{gameMode === "dungeon" ? (
|
||||
{gameMode === "dungeon" && !sharedOnline ? (
|
||||
<small>Respawning resets the current encounter and restores the party.</small>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -122,6 +129,9 @@ export function Hud() {
|
||||
const health = useCombatStore((state) => state.health);
|
||||
const maxHealth = useCombatStore((state) => state.maxHealth);
|
||||
const shield = useCombatStore((state) => state.shield);
|
||||
const namedAbsorb = useCombatStore((state) => state.auras
|
||||
.filter((aura) => aura.targetId === PLAYER_AGGRO_ID)
|
||||
.reduce((total, aura) => total + aura.absorbs.reduce((sum, pool) => sum + pool.remaining, 0), 0));
|
||||
const resource = useCombatStore((state) => state.resource);
|
||||
const maxResource = useCombatStore((state) => state.maxResource);
|
||||
const resourcePools = useCombatStore((state) => state.resourcePools);
|
||||
@@ -132,8 +142,10 @@ export function Hud() {
|
||||
const cooldowns = useCombatStore((state) => state.cooldowns);
|
||||
const globalCooldownEndsAt = useCombatStore((state) => state.globalCooldownEndsAt);
|
||||
const activeCast = useCombatStore((state) => state.activeCast);
|
||||
const pendingGroundTarget = useCombatStore((state) => state.pendingGroundTarget);
|
||||
const feedback = useCombatStore((state) => state.feedback);
|
||||
const target = useCombatStore((state) => state.selectedTargetId ? state.mobs[state.selectedTargetId] ?? null : null);
|
||||
const selectedFriendlyActorId = useCombatStore((state) => state.selectedFriendlyActorId);
|
||||
const friendlyTarget = usePartyStore((state) => state.selectedMemberId
|
||||
? state.members.find((member) => member.id === state.selectedMemberId) ?? null
|
||||
: null);
|
||||
@@ -141,6 +153,7 @@ export function Hud() {
|
||||
const partyMembers = usePartyStore((state) => state.members);
|
||||
const playerRole = usePartyStore((state) => state.playerRole);
|
||||
const settings = useCombatStore((state) => state.settings);
|
||||
const onlineSession = useOnlineSessionStore((state) => state.snapshot);
|
||||
const [clock, setClock] = useState(() => Date.now());
|
||||
|
||||
useEffect(() => {
|
||||
@@ -148,6 +161,15 @@ export function Hud() {
|
||||
return () => window.clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingGroundTarget) return;
|
||||
const cancel = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") useCombatStore.getState().cancelGroundTarget();
|
||||
};
|
||||
window.addEventListener("keydown", cancel);
|
||||
return () => window.removeEventListener("keydown", cancel);
|
||||
}, [pendingGroundTarget]);
|
||||
|
||||
const updateThreatMeterPosition = useCallback((threatMeterPosition: typeof settings.threatMeterPosition) => {
|
||||
useCombatStore.getState().updateSettings({ threatMeterPosition });
|
||||
}, []);
|
||||
@@ -211,11 +233,25 @@ export function Hud() {
|
||||
role: member.role,
|
||||
alive: member.health > 0,
|
||||
})),
|
||||
...(onlineSession?.players ?? [])
|
||||
.filter((player) => player.accountId !== onlineSession?.localAccountId)
|
||||
.map((player) => ({
|
||||
id: onlinePlayerActorId(player.accountId),
|
||||
name: player.character?.name ?? player.username,
|
||||
role: player.role ?? "damage",
|
||||
alive: player.health > 0,
|
||||
})),
|
||||
],
|
||||
target.targetActorId,
|
||||
target.forcedTarget,
|
||||
clock,
|
||||
) : [];
|
||||
const onlineFriendlyAccountId = selectedFriendlyActorId
|
||||
? onlineAccountIdFromActor(selectedFriendlyActorId)
|
||||
: null;
|
||||
const onlineFriendly = onlineFriendlyAccountId
|
||||
? onlineSession?.players.find((player) => player.accountId === onlineFriendlyAccountId) ?? null
|
||||
: null;
|
||||
const selectedFriendly = selfSelected
|
||||
? {
|
||||
name: character?.name ?? "Adventurer",
|
||||
@@ -223,6 +259,7 @@ export function Hud() {
|
||||
health,
|
||||
maxHealth,
|
||||
self: true,
|
||||
targetId: PLAYER_AGGRO_ID,
|
||||
}
|
||||
: friendlyTarget
|
||||
? {
|
||||
@@ -231,7 +268,17 @@ export function Hud() {
|
||||
health: friendlyTarget.health,
|
||||
maxHealth: friendlyTarget.maxHealth,
|
||||
self: false,
|
||||
targetId: friendlyTarget.id,
|
||||
}
|
||||
: onlineFriendly
|
||||
? {
|
||||
name: onlineFriendly.character?.name ?? onlineFriendly.username,
|
||||
detail: `Online player · Level ${onlineFriendly.character?.level ?? 1}`,
|
||||
health: onlineFriendly.health,
|
||||
maxHealth: onlineFriendly.maxHealth,
|
||||
self: false,
|
||||
targetId: onlinePlayerActorId(onlineFriendly.accountId),
|
||||
}
|
||||
: null;
|
||||
const friendlyTargetPercent = selectedFriendly
|
||||
? Math.max(0, Math.min(100, selectedFriendly.health / selectedFriendly.maxHealth * 100))
|
||||
@@ -275,10 +322,27 @@ export function Hud() {
|
||||
character={character ? { ...character, level } : null}
|
||||
health={{ current: health, maximum: maxHealth, label: "Health", tone: "health" }}
|
||||
resource={{ current: resource, maximum: maxResource, label: resourceName, tone: resourceTone }}
|
||||
shield={shield}
|
||||
shield={shield + namedAbsorb}
|
||||
dead={health <= 0}
|
||||
/>
|
||||
<PartyFrames />
|
||||
<SummonFrames now={clock} />
|
||||
|
||||
{pendingGroundTarget ? (
|
||||
<section className="ground-target-reticle" role="status" aria-live="polite">
|
||||
<div aria-hidden="true"><i /><b /></div>
|
||||
<strong>Place ground effect</strong>
|
||||
<small>Move with WASD / left stick. Confirm with Enter / A; Escape / B cancels.</small>
|
||||
<span>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!pendingGroundTarget.valid}
|
||||
onClick={() => useCombatStore.getState().confirmGroundTarget()}
|
||||
>Confirm</button>
|
||||
<button type="button" onClick={() => useCombatStore.getState().cancelGroundTarget()}>Cancel</button>
|
||||
</span>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<Minimap
|
||||
position={playerPosition}
|
||||
@@ -325,11 +389,11 @@ export function Hud() {
|
||||
</header>
|
||||
<div className="target-frame__track"><span style={{ width: `${friendlyTargetPercent}%` }} /></div>
|
||||
<TimedEffectStrip
|
||||
targetId={selectedFriendly.self ? null : friendlyTarget?.id ?? null}
|
||||
targetId={selectedFriendly.self ? null : selectedFriendly.targetId}
|
||||
className="timed-effect-strip--target"
|
||||
/>
|
||||
<AuraStrip
|
||||
targetId={selectedFriendly.self ? PLAYER_AGGRO_ID : friendlyTarget?.id ?? PLAYER_AGGRO_ID}
|
||||
targetId={selectedFriendly.targetId}
|
||||
className="aura-strip--target"
|
||||
/>
|
||||
<small>{Math.ceil(selectedFriendly.health)} / {selectedFriendly.maxHealth}</small>
|
||||
@@ -384,6 +448,7 @@ export function Hud() {
|
||||
gameMode={gameMode}
|
||||
resurrectionCharges={resurrectionCharges}
|
||||
onReturn={returnFromDefeat}
|
||||
sharedOnline={onlineSession !== null}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user