Release Healer Man 0.1.4

This commit is contained in:
phenom committed 2026-08-18 12:06:04 -04:00
1 parent 5bb170039a
commit f4c9cf356c
63 files changed
+2694 -81

No files matched your search

+99
View File
@@ -20,6 +20,11 @@ import {
type WotlkAttackKind,
} from "./wotlkStats";
import { useWailingEncounterStore } from "./wailingCavernsEncounter";
import {
emitCombatPresentation,
type CombatPresentationDelivery,
type CombatPresentationSchool,
} from "./combatPresentation";
const OUT_OF_COMBAT_REGEN_FRACTION = 0.06;
const ATTACK_ACQUISITION_RANGE = 30;
@@ -30,6 +35,43 @@ const TANK_TAUNT_DURATION_MS = 3_000;
let lastAdvanceAt = 0;
let nextRegenAt = 0;
interface PartyPresentationIdentity {
readonly spellId: number;
readonly name: string;
readonly school: CombatPresentationSchool;
readonly delivery: CombatPresentationDelivery;
}
function partyAttackPresentation(member: PartyMember): PartyPresentationIdentity {
if (!partyUsesRangedAttacks(member)) {
const spellId = member.classId === "rogue" ? 1752
: member.classId === "death-knight" ? 45462
: member.classId === "paladin" ? 20271
: 78;
return { spellId, name: "Weapon Attack", school: "physical", delivery: "melee" };
}
if (member.classId === "hunter") return { spellId: 56641, name: "Steady Shot", school: "physical", delivery: "projectile" };
if (member.classId === "mage") return { spellId: 116, name: "Frostbolt", school: "frost", delivery: "projectile" };
if (member.classId === "warlock") return { spellId: 686, name: "Shadow Bolt", school: "shadow", delivery: "projectile" };
if (member.classId === "priest") return { spellId: 585, name: "Smite", school: "holy", delivery: "projectile" };
if (member.classId === "druid") return { spellId: 5176, name: "Wrath", school: "nature", delivery: "projectile" };
return { spellId: 403, name: "Lightning Bolt", school: "nature", delivery: "projectile" };
}
function partyHealPresentation(member: PartyMember): PartyPresentationIdentity {
if (member.classId === "paladin") return { spellId: 635, name: "Holy Light", school: "holy", delivery: "projectile" };
if (member.classId === "shaman") return { spellId: 331, name: "Healing Wave", school: "nature", delivery: "projectile" };
if (member.classId === "druid") return { spellId: 5185, name: "Healing Touch", school: "nature", delivery: "projectile" };
return { spellId: 2061, name: "Flash Heal", school: "holy", delivery: "projectile" };
}
function partyTauntSpellId(member: PartyMember): number {
if (member.classId === "paladin") return 62124;
if (member.classId === "death-knight") return 56222;
if (member.classId === "druid") return 6795;
return 355;
}
export interface PartyBossObjective {
readonly id: string;
readonly name: string;
@@ -455,6 +497,22 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
const tauntSource: ThreatSource = { actorId: member.id, role: "tank" };
if (currentCombat.tauntMob(memberTargetId, tauntSource, TANK_TAUNT_DURATION_MS, now)) {
runtimeMember = { ...runtimeMember, tauntReadyAt: now + TANK_TAUNT_COOLDOWN_MS };
const tauntPresentation = {
occurredAt: now,
actorGroup: "party",
sourceActorId: member.id,
targetActorId: memberTargetId,
abilityId: `party-taunt:${member.id}`,
sourceSpellId: partyTauntSpellId(member),
name: "Taunt",
source: "wow335",
school: "physical",
delivery: "aura",
origin: memberPosition,
targetPosition,
} as const;
emitCombatPresentation({ ...tauntPresentation, phase: "release" });
emitCombatPresentation({ ...tauntPresentation, phase: "aura-start" });
}
}
if (now < member.nextActionAt) {
@@ -483,6 +541,23 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
now,
{ actorId: member.id, role: member.role, abilityId: `party:${member.id}` },
);
const presentation = partyAttackPresentation(member);
const attackPresentation = {
occurredAt: now,
actorGroup: "party",
sourceActorId: member.id,
targetActorId: memberTargetId,
abilityId: `party:${member.id}`,
sourceSpellId: presentation.spellId,
name: presentation.name,
source: "wow335",
school: presentation.school,
delivery: presentation.delivery,
origin: memberPosition,
targetPosition: targetPosition!,
} as const;
emitCombatPresentation({ ...attackPresentation, phase: "release" });
emitCombatPresentation({ ...attackPresentation, phase: "impact" });
nextMembers[index] = {
...runtimeMember,
status: desiredStatus,
@@ -531,6 +606,30 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
{ actorId: healer.id, role: healer.role },
support.effectiveHealing,
);
const presentation = partyHealPresentation(healer);
const healerPosition = getPartyRuntimePosition(healer.id) ?? currentPlayer.playerPosition;
const presentationTargetId = support.targetId === "__player__" ? PLAYER_AGGRO_ID : support.targetId;
const targetPosition = presentationTargetId === PLAYER_AGGRO_ID
? currentPlayer.playerPosition
: presentationTargetId
? getPartyRuntimePosition(presentationTargetId)
: null;
const healPresentation = {
occurredAt: now,
actorGroup: "party",
sourceActorId: healer.id,
...(presentationTargetId ? { targetActorId: presentationTargetId } : {}),
abilityId: `party-heal:${healer.id}`,
sourceSpellId: presentation.spellId,
name: presentation.name,
source: "wow335",
school: presentation.school,
delivery: presentation.delivery,
origin: healerPosition,
targetPosition: targetPosition ?? healerPosition,
} as const;
emitCombatPresentation({ ...healPresentation, phase: "release" });
emitCombatPresentation({ ...healPresentation, phase: "impact" });
}
const targetMapChanged = (