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

+62
View File
@@ -29,6 +29,8 @@ 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 { createDeathKnightRunes, type CombatActorCastState, type DeathKnightRuneState } from "./combatActors";
export {
classCanFillPartyRole,
@@ -56,6 +58,18 @@ export interface PartyMember {
readonly level: number;
readonly health: number;
readonly maxHealth: number;
readonly resourceType: ResourceType;
readonly resourceName: string;
readonly resource: number;
readonly maxResource: number;
readonly resourcePools: Readonly<Partial<Record<ResourceType, number>>>;
readonly lastResourceSpentAt: Readonly<Partial<Record<ResourceType, number>>>;
readonly cooldowns: Readonly<Record<string, number>>;
readonly globalCooldownEndsAt: number;
readonly activeCast: CombatActorCastState | null;
readonly activeCastTargetId: string | null;
readonly schoolLockedUntil: Readonly<Partial<Record<DamageSchool, number>>>;
readonly runes: readonly DeathKnightRuneState[];
readonly gearStats: ItemStats;
readonly status: PartyMemberStatus;
readonly nextActionAt: number;
@@ -214,6 +228,9 @@ function createMember(
const gender: GenderId = genders[Math.floor(nextRandom(identityRandom) * genders.length)] ?? genders[0] ?? "male";
const appearance = randomAppearance(race.id, gender, () => nextRandom(identityRandom));
const maxHealth = memberMaxHealth(level, role, EMPTY_ITEM_STATS, template.classId, race.id);
const resourceProfile = resourceProfileForClass(template.classId);
const maxResource = resourceProfile.maximum;
const resource = resourceProfile.initial;
return {
id: `party-${role}-${index}-${template.classId}`,
name: template.name,
@@ -230,6 +247,18 @@ function createMember(
level: Math.max(1, Math.trunc(level)),
health: maxHealth,
maxHealth,
resourceType: resourceProfile.type,
resourceName: resourceProfile.name,
resource,
maxResource,
resourcePools: Object.freeze({ [resourceProfile.type]: resource }),
lastResourceSpentAt: Object.freeze({}),
cooldowns: Object.freeze({}),
globalCooldownEndsAt: 0,
activeCast: null,
activeCastTargetId: null,
schoolLockedUntil: Object.freeze({}),
runes: template.classId === "death-knight" ? createDeathKnightRunes() : Object.freeze([]),
gearStats: EMPTY_ITEM_STATS,
status: "ready",
nextActionAt: 0,
@@ -321,6 +350,27 @@ export interface PartyState {
cycleMember: (direction?: 1 | -1) => string | null;
clearSelection: () => void;
healMember: (id: string, amount: number) => number;
updateMemberCombat: (
id: string,
patch: Partial<Pick<PartyMember,
| "resource"
| "maxResource"
| "resourcePools"
| "lastResourceSpentAt"
| "cooldowns"
| "globalCooldownEndsAt"
| "activeCast"
| "activeCastTargetId"
| "schoolLockedUntil"
| "runes"
| "health"
| "maxHealth"
| "controlledUntil"
| "controlMechanic"
| "nextActionAt"
| "status"
>>,
) => boolean;
reviveMember: (id: string, percentMaxHealth?: number) => number;
damageMember: (id: string, amount: number, school?: DamageSchool, attackerLevel?: number) => number;
/** Applies damage that has already passed a discrete combat-result roll. */
@@ -479,6 +529,18 @@ export const usePartyStore = create<PartyState>((set, get) => ({
return healed;
},
updateMemberCombat: (id, patch) => {
let updated = false;
set((state) => ({
members: state.members.map((member) => {
if (member.id !== id) return member;
updated = true;
return { ...member, ...patch };
}),
}));
return updated;
},
reviveMember: (id, percentMaxHealth = 0.35) => {
let restored = 0;
set((state) => ({