Update Healer Man server

This commit is contained in:
phenom committed 2026-09-30 13:25:55 -04:00
1 parent 4fb7a0b9c8
commit 4c768fbec6
24 files changed
+654 -22

No files matched your search

+60 -7
View File
@@ -157,6 +157,7 @@ import {
wow335DruidFeralInstinctEnrageAura,
wow335DruidFeralInstinctTigerAura,
} from "./wow335DruidTalentRuntime";
import { WOW335_GLYPH_OF_SWIFTMEND_ID } from "./wow335GlyphCatalog";
import {
WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG,
WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_TAG,
@@ -922,6 +923,7 @@ export interface CharacterCombatSeed {
/** Backward-compatible profile field accepted by the integration bridge. */
readonly experience?: number;
readonly talentRanks?: TalentRanks;
readonly glyphIds?: readonly string[];
readonly actionBindings?: unknown;
readonly inventory?: unknown;
readonly bossCards?: unknown;
@@ -1084,6 +1086,7 @@ export interface CombatState {
lastCombatResult: CombatResult | null;
bossLootRewards: readonly BossLootReward[];
talentRanks: TalentRanks;
glyphIds: readonly string[];
talentModifiers: readonly TalentModifier[];
actionBindings: ActionBindings;
inventory: readonly InventoryItem[];
@@ -1181,6 +1184,7 @@ export interface CombatState {
readonly preserveDefeatedIds?: readonly string[];
}) => void;
allocateTalent: (treeId: string) => boolean;
setGlyphActive: (glyphId: string, active: boolean) => boolean;
resetTalents: () => void;
setActionBinding: (layer: ActionBindingLayer, control: ActionBindingControl, abilityId: string | null) => boolean;
resetActionBindingLayer: (layer: ActionBindingLayer) => void;
@@ -1763,6 +1767,7 @@ function talentAdjustedMaxHealth(
function abilityAttackKind(classId: ClassId, ability: AbilityDefinition): WotlkAttackKind {
if (romWarriorPhysicalWeaponTooltip(ability)) return "melee";
if (ability.effects.some((effect) => effect.kind === "damage" && effect.weaponHand)) return "melee";
if (ability.source === "runewaker") {
const sourceHit = ability.effects.find((effect) => effect.kind === "damage" || effect.kind === "dot");
if (sourceHit && (sourceHit.kind === "damage" || sourceHit.kind === "dot") && sourceHit.school && sourceHit.school !== "physical") {
@@ -7818,6 +7823,7 @@ function initialState() {
lastCombatResult: null,
bossLootRewards: [] as readonly BossLootReward[],
talentRanks,
glyphIds: [],
talentModifiers,
actionBindings: defaultActionBindings(defaultActionBarForClass(classId)),
inventory: [] as readonly InventoryItem[],
@@ -7843,6 +7849,9 @@ export const useCombatStore = create<CombatState>((set, get) => ({
const progression = normalizeProgression(profile.level ?? 1, profile.xp ?? profile.experience ?? 0);
const resource = resourceProfileForClass(classId);
const talentRanks = normalizeTalentRanks(classId, progression.level, profile.talentRanks ?? {});
const glyphIds = classId === "druid" && profile.glyphIds?.includes(WOW335_GLYPH_OF_SWIFTMEND_ID)
? [WOW335_GLYPH_OF_SWIFTMEND_ID]
: [];
const talentModifiers = characterStatModifiers(classId, progression.level, talentRanks);
const abilities = abilitiesForCharacter(classId, secondaryClassId);
const inventory = normalizeInventory(profile.inventory);
@@ -7898,6 +7907,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
deathKnightRunes: classId === "death-knight" ? createDeathKnightRunes() : [],
auras: syncPassiveAuras([], abilities, progression.level, talentRanks, combatNow()),
talentRanks,
glyphIds,
talentModifiers,
actionBindings: withoutUnlearnedTalentBindings(
normalizeActionBindings(profile.actionBindings, defaultActionBarForCharacter(classId, secondaryClassId, progression.level)),
@@ -10243,6 +10253,9 @@ export const useCombatStore = create<CombatState>((set, get) => ({
const shamanLavaLashOffhandSpeedMs = state.classId === "shaman" && ability.name === "Lava Lash"
? playerEquippedMeleeWeapon(state, "off-hand")?.speedMs
: undefined;
const shamanStormstrikeOffhandWeapon = state.classId === "shaman" && ability.name === "Stormstrike"
? playerEquippedMeleeWeapon(state, "off-hand")
: null;
let shamanTidalWavesProcAttempted = false;
const combatEventIdBeforeAbilityEffects = work.combatEvents.at(-1)?.id ?? 0;
let suddenDoomTrigger: ReturnType<typeof wow335DeathKnightSuddenDoomTriggerForLandedHit> = null;
@@ -10267,6 +10280,20 @@ export const useCombatStore = create<CombatState>((set, get) => ({
)
) continue;
if (effect.kind === "damage") {
const weaponHitProfile = effect.weaponHand === "main-hand"
? playerAutoAttackProfile(
state,
aurasForEntity(work.auras, PLAYER_AURA_ENTITY_ID),
playerEquippedMeleeWeapon(state, "main-hand") ?? undefined,
)
: effect.weaponHand === "off-hand" && shamanStormstrikeOffhandWeapon
? playerOffhandAutoAttackProfile(
state,
shamanStormstrikeOffhandWeapon,
aurasForEntity(work.auras, PLAYER_AURA_ENTITY_ID),
)
: null;
if (effect.weaponHand && !weaponHitProfile) continue;
const amount = talentAdjustedAmount(
(() => {
const conflagrateDot = state.classId === "warlock" && ability.name === "Conflagrate"
@@ -10274,7 +10301,9 @@ export const useCombatStore = create<CombatState>((set, get) => ({
: null;
const sourceAbility = conflagrateDot ? abilityById(conflagrateDot.sourceAbilityId) : null;
const burstSeconds = sourceAbility?.name === "Immolate" ? 12 : 8;
const baseDamage = conflagrateDot && sourceAbility
const baseDamage = weaponHitProfile
? weaponHitProfile.damage * effect.coefficient
: conflagrateDot && sourceAbility
? (conflagrateDot.fixedAmount ?? equipmentAdjustedCombatAmount(
conflagrateDot,
work.progression.level,
@@ -10326,13 +10355,19 @@ export const useCombatStore = create<CombatState>((set, get) => ({
castThreatSource,
effectSchool === "physical" ? attackKind : "spell",
effectSchool,
`${castThreatSource.actorId}:${ability.id}:${id}:${now}:direct`,
`${castThreatSource.actorId}:${ability.id}:${id}:${now}:direct${effect.weaponHand ? `:${effect.weaponHand}` : ""}`,
ability,
false,
ability.name === "Shiv" || (consumesSurgeOfLight && ability.name === "Smite")
? { canCrit: false } : {},
mageArcanePotencyCriticalChanceBonusPercent,
shamanLavaLashOffhandSpeedMs,
effect.weaponHand === "main-hand"
? playerEquippedMeleeWeapon(state, "main-hand")?.speedMs
?? work.gearStats.meleeWeapon?.speedMs
?? WOW335_SHAMAN_MAELSTROM_WEAPON_DEFAULT_SPEED_MS
: effect.weaponHand === "off-hand"
? shamanStormstrikeOffhandWeapon?.speedMs
: shamanLavaLashOffhandSpeedMs,
);
if (state.classId === "death-knight"
&& effect.kind === "damage"
@@ -10678,7 +10713,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
healingTargetHealth,
healingTargetMaxHealth,
);
if (swiftmendHot) {
if (swiftmendHot && !state.glyphIds.includes(WOW335_GLYPH_OF_SWIFTMEND_ID)) {
work.effects = work.effects.filter((candidate) => candidate.id !== swiftmendHot.id);
}
const riptideAdjustedAmount = shamanRiptideHot
@@ -16405,10 +16440,11 @@ export const useCombatStore = create<CombatState>((set, get) => ({
&& (!autoAttackSource || isPositionInFacingArc(autoAttackSource, autoAttackTargetPosition ?? state.playerPosition, Math.PI))
);
if (canAutoAttack) {
const mainHandWeapon = state.classId === "rogue"
const supportsPlayerOffhandSwings = state.classId === "rogue" || state.classId === "shaman";
const mainHandWeapon = supportsPlayerOffhandSwings
? playerEquippedMeleeWeapon(state, "main-hand")
: null;
const offHandWeapon = state.classId === "rogue"
const offHandWeapon = supportsPlayerOffhandSwings
? playerEquippedMeleeWeapon(state, "off-hand")
: null;
const profile = playerAutoAttackProfile(
@@ -16433,6 +16469,9 @@ export const useCombatStore = create<CombatState>((set, get) => ({
}
if (now >= nextAutoAttackAt) {
const isDualWielding = Boolean(offHandWeapon);
const mainHandSpeedMs = mainHandWeapon?.speedMs
?? state.gearStats.meleeWeapon?.speedMs
?? WOW335_SHAMAN_MAELSTROM_WEAPON_DEFAULT_SPEED_MS;
const autoAttackSchool = mainHandWeapon?.damageSchool
?? (offHandWeapon ? "physical" : state.gearStats.meleeWeapon?.damageSchool)
?? "physical";
@@ -16449,6 +16488,8 @@ export const useCombatStore = create<CombatState>((set, get) => ({
undefined,
false,
{ isAutoAttack: true, isDualWielding },
0,
mainHandSpeedMs,
);
let autoAttackKilled = result.killed;
const enchantedThrowActive = work.auras.some((aura) => (
@@ -16542,7 +16583,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
nextOffhandAutoAttackAt = 0;
}
}
if (state.classId !== "rogue" || !offHandWeapon || !autoAttackTargetId) {
if (!supportsPlayerOffhandSwings || !offHandWeapon || !autoAttackTargetId) {
nextOffhandAutoAttackAt = 0;
} else if (now >= nextOffhandAutoAttackAt) {
const offhandProfile = playerOffhandAutoAttackProfile(
@@ -16565,6 +16606,8 @@ export const useCombatStore = create<CombatState>((set, get) => ({
undefined,
false,
{ isAutoAttack: true, isDualWielding: true },
0,
offHandWeapon.speedMs,
);
if (result.combatResult?.landed) {
maybeProcRogueCombatPotencyInWork(work, offhandSeed, now);
@@ -17910,6 +17953,16 @@ export const useCombatStore = create<CombatState>((set, get) => ({
};
}),
setGlyphActive: (glyphId, active) => {
const state = get();
if (state.classId !== "druid" || glyphId !== WOW335_GLYPH_OF_SWIFTMEND_ID) return false;
const glyphIds = active
? [WOW335_GLYPH_OF_SWIFTMEND_ID]
: state.glyphIds.filter((id) => id !== WOW335_GLYPH_OF_SWIFTMEND_ID);
if (glyphIds.length !== state.glyphIds.length) set({ glyphIds });
return true;
},
allocateTalent: (treeId) => {
const state = get();
const result = allocateTalentPoint(state.talentRanks, state.classId, treeId, state.level);