Release Healer Man 0.1.6

This commit is contained in:
phenom committed 2026-08-20 14:05:50 -04:00
1 parent 55cfd43d66
commit b12fb84859
96 files changed
+10874 -609

No files matched your search

+137
View File
@@ -0,0 +1,137 @@
import type { ClassId } from "../app/characterCatalog";
import type {
OnlineMobTransform,
OnlineRelayEventInput,
OnlineSessionPlayer,
} from "../app/onlineSessionTypes";
import {
normalizeActorResourcePool,
type CombatActorState,
} from "./combatActors";
const ONLINE_PLAYER_PREFIX = "online-player:";
let active = false;
let authority = true;
let localAccountId: string | null = null;
let playerActors = new Map<string, CombatActorState>();
let players = new Map<string, OnlineSessionPlayer>();
let mobTransforms = new Map<string, OnlineMobTransform>();
let interactionSequence = 0;
let interactions: OnlineRelayEventInput[] = [];
export function onlinePlayerActorId(accountId: string): string {
return `${ONLINE_PLAYER_PREFIX}${accountId}`;
}
export function onlineAccountIdFromActor(actorId: string): string | null {
return actorId.startsWith(ONLINE_PLAYER_PREFIX)
? actorId.slice(ONLINE_PLAYER_PREFIX.length)
: null;
}
export function isOnlinePlayerActorId(actorId: string): boolean {
return actorId.startsWith(ONLINE_PLAYER_PREFIX);
}
export function configureOnlineSessionRuntime(input: {
readonly active: boolean;
readonly authority: boolean;
readonly localAccountId: string | null;
readonly players?: readonly OnlineSessionPlayer[];
readonly mobTransforms?: Readonly<Record<string, OnlineMobTransform>>;
}): void {
active = input.active;
authority = !input.active || input.authority;
localAccountId = input.localAccountId;
players = new Map((input.players ?? []).map((player) => [player.accountId, player]));
mobTransforms = new Map(Object.entries(input.mobTransforms ?? {}));
const nextActors = new Map<string, CombatActorState>();
for (const player of input.players ?? []) {
if (player.accountId === input.localAccountId || !player.character) continue;
const id = onlinePlayerActorId(player.accountId);
const maximum = Math.max(0, player.maxResource);
const resourceId = player.resourceName.toLowerCase() || "resource";
nextActors.set(id, {
id,
threatActorId: id,
kind: "companion",
ownerId: player.accountId,
classId: player.character.classId as ClassId,
level: player.character.level,
health: player.health,
maxHealth: player.maxHealth,
absorb: 0,
resources: maximum > 0
? { [resourceId]: normalizeActorResourcePool(resourceId, player.resource, maximum) }
: {},
cooldowns: {},
globalCooldownEndsAt: 0,
cast: player.activeCast ? {
abilityId: player.activeCast.abilityId,
school: player.activeCast.school,
mode: player.activeCast.mode,
startedAt: player.activeCast.startedAt,
completesAt: player.activeCast.completesAt,
originalDurationMs: player.activeCast.originalDurationMs,
pushbackHits: player.activeCast.pushbackHits,
origin: player.activeCast.origin,
targetId: player.activeCast.targetId,
} : null,
controlledUntil: 0,
schoolLockedUntil: {},
comboPoints: 0,
comboTargetId: null,
runes: [],
alive: player.health > 0,
});
}
playerActors = nextActors;
if (!input.active) interactions = [];
}
export function onlineSessionIsActive(): boolean {
return active;
}
export function onlineSessionOwnsWorldSimulation(): boolean {
return !active || authority;
}
export function onlineSessionLocalAccountId(): string | null {
return localAccountId;
}
export function onlinePlayerRuntimeActors(): readonly CombatActorState[] {
return [...playerActors.values()];
}
export function onlinePlayerRuntimeActor(actorId: string): CombatActorState | null {
return playerActors.get(actorId) ?? null;
}
export function onlineSessionPlayer(accountId: string): OnlineSessionPlayer | null {
return players.get(accountId) ?? null;
}
export function onlineSynchronizedMobTransform(id: string): OnlineMobTransform | null {
return active && !authority ? mobTransforms.get(id) ?? null : null;
}
export function requestOnlineInteraction(kind: "loot" | "portal", targetActorId = ""): boolean {
if (!active || authority) return false;
interactionSequence += 1;
interactions.push({
clientEventId: `interaction:${kind}:${Date.now()}:${interactionSequence}`,
kind,
sourceActorId: localAccountId ? onlinePlayerActorId(localAccountId) : "",
targetActorId,
occurredAt: Date.now(),
});
return true;
}
export function drainOnlineInteractions(): readonly OnlineRelayEventInput[] {
const pending = interactions;
interactions = [];
return pending;
}