Update class mechanics, stat rulesets, and CoA reference catalogs

This commit is contained in:
phenom committed 2026-10-04 12:56:30 -04:00
1 parent b16821e92e
commit 39458d5de6
221 files changed
+2584962 -568

No files matched your search

+47 -14
View File
@@ -4,12 +4,19 @@ import {
type CombatActorState,
} from "./combatActors";
import type { PartyMember } from "./partyStore";
import { baseClassFor, isRomClassId } from "../app/characterCatalog";
import { isRomClassId } from "../app/characterCatalog";
import { deriveCharacterStats } from "./wotlkStats";
export function companionResourceMaximum(member: PartyMember, id: string): number {
return id === member.resourceType ? member.maxResource : id === "mana" && !isRomClassId(member.classId)
? deriveCharacterStats(baseClassFor(member.classId), member.level, member.gearStats, member.raceId).mana
if (id === member.resourceType) return member.maxResource;
const mirroredMaximum = member.resourceMaximums?.[id as ResourceType];
if (mirroredMaximum !== undefined) return mirroredMaximum;
return id === "mana"
&& (member.statRuleset === "rom" || !isRomClassId(member.classId))
? deriveCharacterStats(
member.classId, member.level, member.gearStats, member.raceId, {},
member.secondaryClassId, member.gender, member.statRuleset ?? "wow",
).mana
: id === "nature-power" ? naturePowerMaximumForCharacter(member.classId, member.secondaryClassId, member.level)
: id === "psi" ? 6
: 100;
@@ -25,23 +32,47 @@ export function companionActorFromMember(
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,
companionResourceMaximum(member, id),
id === member.resourceType ? profile.regenerationPerSecond : 0,
member.lastResourceSpentAt[id as ResourceType] ?? 0,
),
]));
let resources: CombatActorState["resources"];
if (existing?.kind === "companion" && member.statRuleset === "rom" && isRomClassId(member.classId)) {
const previousResources = existing.resources;
const nextResources: Record<string, ReturnType<typeof normalizeActorResourcePool>> = {};
const resourceIds = new Set([...Object.keys(previousResources), ...Object.keys(member.resourcePools)]);
for (const id of resourceIds) {
const previous = previousResources[id];
const nextMaximum = companionResourceMaximum(member, id);
const hasChangedMaximum = previous !== undefined && Math.abs(previous.maximum - nextMaximum) > 1e-6;
const current = hasChangedMaximum
? member.resourcePools[id as ResourceType] ?? previous.current
: previous?.current ?? member.resourcePools[id as ResourceType] ?? 0;
nextResources[id] = normalizeActorResourcePool(
id,
current,
nextMaximum,
id === member.resourceType ? profile.regenerationPerSecond : previous?.regenerationPerSecond ?? 0,
member.lastResourceSpentAt[id as ResourceType] ?? previous?.lastSpentAt ?? 0,
);
}
resources = nextResources;
} else if (existing?.kind === "companion") {
resources = existing.resources;
} else {
resources = Object.fromEntries(Object.entries(member.resourcePools).map(([id, current]) => [id,
normalizeActorResourcePool(
id,
current ?? 0,
companionResourceMaximum(member, id),
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,
statRuleset: member.statRuleset,
level: member.level,
health: actor?.health ?? member.health,
maxHealth: actor?.maxHealth ?? member.maxHealth,
@@ -64,6 +95,7 @@ export function companionActorFromMember(
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 resourceMaximums = Object.fromEntries(Object.entries(actor.resources).map(([id, pool]) => [id, pool.maximum])) 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 {
@@ -73,6 +105,7 @@ export function memberFromCompanionActor(member: PartyMember, actor?: CombatActo
resource: primary?.current ?? member.resource,
maxResource: primary?.maximum ?? member.maxResource,
resourcePools,
resourceMaximums,
lastResourceSpentAt,
comboPoints: actor.comboPoints,
comboTargetId: actor.comboTargetId,