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

+105
View File
@@ -0,0 +1,105 @@
import { resourceProfileForClass, type ResourceType } from "./abilityCatalog";
import {
normalizeActorResourcePool,
type CombatActorState,
} from "./combatActors";
import type { PartyMember } from "./partyStore";
/**
* Creates the authoritative companion actor. Existing actor values win over
* the party-frame compatibility record once the actor has entered combat.
*/
export function companionActorFromMember(
member: PartyMember,
absorb = 0,
existing?: CombatActorState | null,
): CombatActorState {
const profile = resourceProfileForClass(member.classId);
const resources = existing?.kind === "companion"
? existing.resources
: Object.fromEntries(Object.entries(member.resourcePools).map(([id, current]) => [id,
normalizeActorResourcePool(
id,
current ?? 0,
id === member.resourceType ? member.maxResource : 100,
id === member.resourceType ? profile.regenerationPerSecond : 0,
member.lastResourceSpentAt[id as ResourceType] ?? 0,
),
]));
const actor = existing?.kind === "companion" ? existing : null;
return Object.freeze({
id: member.id,
threatActorId: member.id,
kind: "companion" as const,
classId: member.classId,
level: member.level,
health: actor?.health ?? member.health,
maxHealth: actor?.maxHealth ?? member.maxHealth,
absorb,
resources,
cooldowns: actor?.cooldowns ?? member.cooldowns,
globalCooldownEndsAt: actor?.globalCooldownEndsAt ?? member.globalCooldownEndsAt,
cast: actor?.cast ?? member.activeCast,
controlledUntil: actor?.controlledUntil ?? member.controlledUntil,
schoolLockedUntil: actor?.schoolLockedUntil ?? member.schoolLockedUntil,
comboPoints: actor?.comboPoints ?? 0,
comboTargetId: actor?.comboTargetId ?? null,
runes: actor?.runes ?? member.runes,
abilityIds: actor?.abilityIds,
alive: (actor?.health ?? member.health) > 0,
});
}
/** Party and scene consumers receive a read-compatible mirror of actor state. */
export function memberFromCompanionActor(member: PartyMember, actor?: CombatActorState | null): PartyMember {
if (!actor || actor.kind !== "companion") return member;
const resourcePools = Object.fromEntries(Object.entries(actor.resources).map(([id, pool]) => [id, pool.current])) as Partial<Record<ResourceType, number>>;
const lastResourceSpentAt = Object.fromEntries(Object.entries(actor.resources).map(([id, pool]) => [id, pool.lastSpentAt])) as Partial<Record<ResourceType, number>>;
const primary = actor.resources[member.resourceType];
return {
...member,
health: actor.health,
maxHealth: actor.maxHealth,
resource: primary?.current ?? member.resource,
maxResource: primary?.maximum ?? member.maxResource,
resourcePools,
lastResourceSpentAt,
cooldowns: actor.cooldowns,
globalCooldownEndsAt: actor.globalCooldownEndsAt,
activeCast: actor.cast,
activeCastTargetId: actor.cast?.targetId ?? member.activeCastTargetId,
schoolLockedUntil: actor.schoolLockedUntil,
runes: actor.runes,
controlledUntil: actor.controlledUntil,
status: actor.health <= 0 ? "down" : member.status,
};
}
export function companionActorWithMemberRuntime(
actor: CombatActorState,
member: PartyMember,
): CombatActorState {
const profile = resourceProfileForClass(member.classId);
const resources = Object.fromEntries(Object.entries(member.resourcePools).map(([id, current]) => [id,
normalizeActorResourcePool(
id,
current ?? 0,
id === member.resourceType ? member.maxResource : actor.resources[id]?.maximum ?? 100,
id === member.resourceType ? profile.regenerationPerSecond : actor.resources[id]?.regenerationPerSecond ?? 0,
member.lastResourceSpentAt[id as ResourceType] ?? actor.resources[id]?.lastSpentAt ?? 0,
),
]));
return Object.freeze({
...actor,
health: member.health,
maxHealth: member.maxHealth,
resources,
cooldowns: member.cooldowns,
globalCooldownEndsAt: member.globalCooldownEndsAt,
cast: member.activeCast ? { ...member.activeCast, targetId: member.activeCastTargetId } : null,
controlledUntil: member.controlledUntil,
schoolLockedUntil: member.schoolLockedUntil,
runes: member.runes,
alive: member.health > 0,
});
}