forked from phenom/healer-man
Release Healer Man 0.1.6
This commit is contained in:
1 parent
55cfd43d66
commit
b12fb84859
96 files changed
+10874
-609
No files matched your search
@@ -0,0 +1,117 @@
|
||||
import type { ClassId } from "../app/characterCatalog";
|
||||
import type { DamageSchool } from "./combatAuras";
|
||||
|
||||
export type CombatActorKind = "player" | "companion" | "enemy" | "pet" | "totem" | "summon";
|
||||
export type CombatResourceId = string;
|
||||
export type DeathKnightRuneType = "blood" | "frost" | "unholy" | "death";
|
||||
export type CombatActorPosition = readonly [number, number, number];
|
||||
|
||||
export interface CombatResourcePoolState {
|
||||
readonly id: CombatResourceId;
|
||||
readonly current: number;
|
||||
readonly maximum: number;
|
||||
readonly regenerationPerSecond: number;
|
||||
/** Mana regeneration observes the five-second rule from this timestamp. */
|
||||
readonly lastSpentAt: number;
|
||||
}
|
||||
|
||||
export interface DeathKnightRuneState {
|
||||
readonly slot: number;
|
||||
readonly baseType: Exclude<DeathKnightRuneType, "death">;
|
||||
readonly type: DeathKnightRuneType;
|
||||
readonly readyAt: number;
|
||||
}
|
||||
|
||||
export interface CombatActorCastState {
|
||||
readonly abilityId: string;
|
||||
readonly school: DamageSchool;
|
||||
readonly mode: "cast" | "channel";
|
||||
readonly startedAt: number;
|
||||
readonly completesAt: number;
|
||||
readonly originalDurationMs: number;
|
||||
readonly pushbackHits: number;
|
||||
/** Simulation-space origin used for movement cancellation and completion validation. */
|
||||
readonly origin?: CombatActorPosition;
|
||||
readonly targetId?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer-free snapshot shared by every combatant. Specialized player, party,
|
||||
* and mob records may retain presentation fields, but combat rules consume
|
||||
* this common contract.
|
||||
*/
|
||||
export interface CombatActorState {
|
||||
readonly id: string;
|
||||
/** Stable id used by threat tables even when a renderer entity is replaced. */
|
||||
readonly threatActorId?: string;
|
||||
readonly kind: CombatActorKind;
|
||||
readonly ownerId?: string;
|
||||
readonly classId?: ClassId;
|
||||
readonly level: number;
|
||||
readonly health: number;
|
||||
readonly maxHealth: number;
|
||||
readonly absorb: number;
|
||||
readonly resources: Readonly<Record<CombatResourceId, CombatResourcePoolState>>;
|
||||
readonly cooldowns: Readonly<Record<string, number>>;
|
||||
readonly globalCooldownEndsAt: number;
|
||||
readonly cast: CombatActorCastState | null;
|
||||
readonly controlledUntil: number;
|
||||
readonly schoolLockedUntil: Readonly<Partial<Record<DamageSchool, number>>>;
|
||||
readonly comboPoints: number;
|
||||
readonly comboTargetId: string | null;
|
||||
readonly runes: readonly DeathKnightRuneState[];
|
||||
/** Executable catalog abilities owned by pets, summons, and companions. */
|
||||
readonly abilityIds?: readonly string[];
|
||||
readonly alive: boolean;
|
||||
}
|
||||
|
||||
export function createDeathKnightRunes(now = 0): readonly DeathKnightRuneState[] {
|
||||
const types = ["blood", "blood", "frost", "frost", "unholy", "unholy"] as const;
|
||||
return Object.freeze(types.map((type, slot) => Object.freeze({
|
||||
slot,
|
||||
baseType: type,
|
||||
type,
|
||||
readyAt: Math.max(0, now),
|
||||
})));
|
||||
}
|
||||
|
||||
export function normalizeActorResourcePool(
|
||||
id: CombatResourceId,
|
||||
current: number,
|
||||
maximum: number,
|
||||
regenerationPerSecond = 0,
|
||||
lastSpentAt = 0,
|
||||
): CombatResourcePoolState {
|
||||
const safeMaximum = Math.max(0, Number.isFinite(maximum) ? maximum : 0);
|
||||
return Object.freeze({
|
||||
id,
|
||||
current: Math.max(0, Math.min(safeMaximum, Number.isFinite(current) ? current : 0)),
|
||||
maximum: safeMaximum,
|
||||
regenerationPerSecond: Math.max(0, Number.isFinite(regenerationPerSecond) ? regenerationPerSecond : 0),
|
||||
lastSpentAt: Math.max(0, Number.isFinite(lastSpentAt) ? lastSpentAt : 0),
|
||||
});
|
||||
}
|
||||
|
||||
export function spendActorResource(
|
||||
pool: CombatResourcePoolState,
|
||||
amount: number,
|
||||
now: number,
|
||||
): CombatResourcePoolState | null {
|
||||
const requested = Math.max(0, Number.isFinite(amount) ? amount : 0);
|
||||
if (pool.current + Number.EPSILON < requested) return null;
|
||||
return Object.freeze({
|
||||
...pool,
|
||||
current: Math.max(0, pool.current - requested),
|
||||
lastSpentAt: Math.max(pool.lastSpentAt, Number.isFinite(now) ? now : 0),
|
||||
});
|
||||
}
|
||||
|
||||
export function restoreActorResource(
|
||||
pool: CombatResourcePoolState,
|
||||
amount: number,
|
||||
): CombatResourcePoolState {
|
||||
return Object.freeze({
|
||||
...pool,
|
||||
current: Math.min(pool.maximum, pool.current + Math.max(0, Number.isFinite(amount) ? amount : 0)),
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user