Release Healer Man 0.1.53

This commit is contained in:
phenom committed 2026-09-18 12:32:51 -04:00
1 parent d0a02487db
commit d6976e6ef6
17 files changed
+555 -69

No files matched your search

+51 -9
View File
@@ -31,7 +31,12 @@ import type { RuntimeTalentRanks as TalentRanks } from "./talentRuntimeCatalog";
import type { EnemyControlMechanic } from "./enemyAttacks";
import type { ContentCategoryId } from "../app/contentCategories";
import type { PartyRole, PartySize } from "./partyRoles";
import { resourceProfileForClass, type ResourceType } from "./abilityCatalog";
import {
abilitiesForCharacter,
learnedAbilityForBuild,
resourceProfileForClass,
type ResourceType,
} from "./abilityCatalog";
import { createDeathKnightRunes, type CombatActorCastState, type DeathKnightRuneState } from "./combatActors";
export {
@@ -222,6 +227,43 @@ function memberMaxResource(
: profile.maximum;
}
function secondaryResourceMaximum(type: ResourceType): number {
if (type === "nature-power") return 10;
if (type === "psi") return 6;
return 100;
}
function initialSecondaryResource(type: ResourceType, maximum: number): number {
return type === "rage" || type === "runic-power" || type === "nature-power" || type === "psi"
? 0
: maximum;
}
/** Keeps every resource used by the member's learned kit available to companion AI. */
function withMemberAbilityResourcePools(member: PartyMember): PartyMember {
const types = new Set<ResourceType>([member.resourceType]);
for (const ability of abilitiesForCharacter(member.classId, member.secondaryClassId)) {
const learned = learnedAbilityForBuild(ability, member.level, member.talentRanks);
if (!learned) continue;
for (const cost of learned.costs?.length ? learned.costs : [learned.cost]) {
if (cost.resource !== "health") types.add(cost.resource);
}
}
const resourcePools: Partial<Record<ResourceType, number>> = {};
for (const type of types) {
if (type === member.resourceType) {
resourcePools[type] = member.resource;
continue;
}
const maximum = secondaryResourceMaximum(type);
resourcePools[type] = Math.max(0, Math.min(
maximum,
member.resourcePools[type] ?? initialSecondaryResource(type, maximum),
));
}
return { ...member, resourcePools: Object.freeze(resourcePools) };
}
function withMemberGearStats(member: PartyMember, gearStats: ItemStats): PartyMember {
const maxHealth = memberMaxHealth(
member.level,
@@ -237,7 +279,7 @@ function withMemberGearStats(member: PartyMember, gearStats: ItemStats): PartyMe
const resource = Math.max(0, Math.round(
member.resource / Math.max(1, member.maxResource) * maxResource,
));
return {
return withMemberAbilityResourcePools({
...member,
gearStats,
health,
@@ -245,7 +287,7 @@ function withMemberGearStats(member: PartyMember, gearStats: ItemStats): PartyMe
resource,
maxResource,
resourcePools: Object.freeze({ ...member.resourcePools, [member.resourceType]: resource }),
};
});
}
function createMember(
@@ -286,7 +328,7 @@ function createMember(
const resource = Math.round(
maxResource * (resourceProfile.maximum > 0 ? resourceProfile.initial / resourceProfile.maximum : 0),
);
return {
return withMemberAbilityResourcePools({
id,
name: template.name,
raceId: race.id,
@@ -323,7 +365,7 @@ function createMember(
tauntReadyAt: 0,
controlledUntil: 0,
controlMechanic: null,
};
});
}
/** Creates one exact companion while retaining the live party stat/equipment path. */
@@ -339,12 +381,12 @@ export function createConfiguredPartyMember(
specialization: configuration.specialization,
talentTreeId: configuration.talentTreeId ?? configuration.specializationId ?? "",
}, configuration.role, level, index, seed, configuration.talentRanks);
return Object.freeze({
return Object.freeze(withMemberAbilityResourcePools({
...member,
secondaryClassId: configuration.secondaryClassId ?? null,
specializationId: configuration.specializationId ?? member.specializationId,
talentRanks: configuration.talentRanks ?? member.talentRanks,
});
}));
}
/**
@@ -571,7 +613,7 @@ export const usePartyStore = create<PartyState>((set, get) => ({
const resource = Math.max(0, Math.round(
member.resource / Math.max(1, member.maxResource) * maxResource,
));
return {
return withMemberAbilityResourcePools({
...member,
level: safeLevel,
health,
@@ -582,7 +624,7 @@ export const usePartyStore = create<PartyState>((set, get) => ({
talentRanks: isRomClassId(member.classId)
? Object.freeze({})
: runtimePartyTalentRanksForLevel(member.classId, member.specialization, safeLevel),
};
});
}),
};
}),