forked from phenom/healer-man
Release Healer Man 0.1.37
This commit is contained in:
1 parent
03a4e3f51c
commit
361734bc41
41 files changed
+1714
-110
No files matched your search
@@ -0,0 +1,84 @@
|
||||
import { create } from "zustand";
|
||||
import { classById, CLASSES } from "../app/characterCatalog";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
import { useOnlineSessionStore } from "../app/onlineSessionStore";
|
||||
import { PLAYER_AGGRO_ID } from "./aggro";
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { subscribeCombatEvents } from "./combatEvents";
|
||||
import { advanceDpsMeter, dpsMeterSnapshot, emptyDpsMeter, recordDpsDamage, type DpsMeterActor } from "./dpsMeter";
|
||||
import { onlinePlayerActorId } from "./onlineSessionRuntime";
|
||||
import { usePartyStore } from "./partyStore";
|
||||
import { useGameStore } from "./store";
|
||||
|
||||
export const useDpsMeterStore = create(() => emptyDpsMeter());
|
||||
|
||||
function meterActors(): DpsMeterActor[] {
|
||||
const combat = useCombatStore.getState();
|
||||
const online = useOnlineSessionStore.getState().snapshot;
|
||||
return [
|
||||
{ id: PLAYER_AGGRO_ID, name: useShellStore.getState().activeCharacter?.name ?? "You", color: classById(combat.classId).color },
|
||||
...usePartyStore.getState().members.map((member) => ({ id: member.id, name: member.name, color: classById(member.classId).color })),
|
||||
...(online?.players ?? []).filter((player) => player.accountId !== online?.localAccountId).map((player) => ({
|
||||
id: onlinePlayerActorId(player.accountId), name: player.character?.name ?? player.username,
|
||||
color: CLASSES.find((entry) => entry.id === player.character?.classId)?.color ?? "#d5c28b",
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
export function dpsSourceActor(sourceId: string): DpsMeterActor | null {
|
||||
const combat = useCombatStore.getState();
|
||||
const localAccount = useOnlineSessionStore.getState().snapshot?.localAccountId;
|
||||
const visited = new Set<string>();
|
||||
let id = sourceId;
|
||||
while (!visited.has(id)) {
|
||||
visited.add(id);
|
||||
if (localAccount && id === onlinePlayerActorId(localAccount)) id = PLAYER_AGGRO_ID;
|
||||
const known = meterActors().find((actor) => actor.id === id);
|
||||
if (known) return known;
|
||||
const ownerId = combat.actors[id]?.ownerId ?? combat.summons.find((summon) => summon.id === id)?.ownerActorId;
|
||||
if (!ownerId) return null;
|
||||
id = ownerId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function currentDpsMeterSnapshot() {
|
||||
return dpsMeterSnapshot(useDpsMeterStore.getState(), meterActors());
|
||||
}
|
||||
|
||||
/** Owned by the gameplay runtime so hiding or moving the meter never loses damage. */
|
||||
export function connectDpsMeter(): () => void {
|
||||
useDpsMeterStore.setState(emptyDpsMeter(), true);
|
||||
let suspendedPreviously = false;
|
||||
const sample = () => {
|
||||
const combat = useCombatStore.getState();
|
||||
const game = useGameStore.getState();
|
||||
const inCombat = Object.values(combat.mobs).some((mob) => mob.engaged && !mob.dead && !mob.despawned);
|
||||
const sharedOnline = Boolean(useOnlineSessionStore.getState().snapshot);
|
||||
const suspended = !sharedOnline && (game.paused || game.companionOpen || (typeof document !== "undefined" && document.hidden));
|
||||
const state = useDpsMeterStore.getState();
|
||||
const next = advanceDpsMeter(state, Date.now(), inCombat, suspendedPreviously);
|
||||
suspendedPreviously = suspended;
|
||||
if (next !== state) useDpsMeterStore.setState(next, true);
|
||||
};
|
||||
const unsubscribeEvents = subscribeCombatEvents((event) => {
|
||||
if (event.kind !== "damage" || !useCombatStore.getState().mobs[event.targetActorId]) return;
|
||||
const actor = dpsSourceActor(event.sourceActorId);
|
||||
if (actor) useDpsMeterStore.setState(recordDpsDamage(useDpsMeterStore.getState(), event, actor), true);
|
||||
});
|
||||
const unsubscribeCombat = useCombatStore.subscribe((state, previous) => {
|
||||
if (state.combatEvents !== previous.combatEvents && state.combatEvents.length === 0) {
|
||||
useDpsMeterStore.setState(emptyDpsMeter(), true);
|
||||
} else sample();
|
||||
});
|
||||
const unsubscribeGame = useGameStore.subscribe(sample);
|
||||
if (typeof document !== "undefined") document.addEventListener("visibilitychange", sample);
|
||||
const timer = setInterval(sample, 250);
|
||||
sample();
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
unsubscribeEvents(); unsubscribeCombat(); unsubscribeGame();
|
||||
if (typeof document !== "undefined") document.removeEventListener("visibilitychange", sample);
|
||||
useDpsMeterStore.setState(emptyDpsMeter(), true);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user