Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -0,0 +1,118 @@
|
||||
import { runtimeTalentNodeById } from "./talentRuntimeCatalog";
|
||||
|
||||
export const WOW335_HUNTER_GO_FOR_THE_THROAT_TALENT_ID = "talent-1818";
|
||||
export const WOW335_HUNTER_GO_FOR_THE_THROAT_INTERNAL_COOLDOWN_MS = 2_000;
|
||||
|
||||
const HUNTER_GO_FOR_THE_THROAT_DIRECT_ATTACK_KINDS: Readonly<Record<string, "melee" | "ranged">> = Object.freeze({
|
||||
"wow335-hunter-aimed-shot": "ranged",
|
||||
"wow335-hunter-arcane-shot": "ranged",
|
||||
"wow335-hunter-auto-shot": "ranged",
|
||||
"wow335-hunter-chimera-shot": "ranged",
|
||||
"wow335-hunter-counterattack": "melee",
|
||||
"wow335-hunter-kill-shot": "ranged",
|
||||
"wow335-hunter-mongoose-bite": "melee",
|
||||
"wow335-hunter-multi-shot": "ranged",
|
||||
"wow335-hunter-raptor-strike": "melee",
|
||||
"wow335-hunter-scatter-shot": "ranged",
|
||||
"wow335-hunter-silencing-shot": "ranged",
|
||||
"wow335-hunter-steady-shot": "ranged",
|
||||
// Simulated Hunter weapon swings use these runtime event IDs rather than
|
||||
// the Auto Shot spell row; the player path currently reports its generic
|
||||
// basic swing as melee, while party Hunters report their basic swing ranged.
|
||||
"party-auto-attack": "ranged",
|
||||
"player-auto-attack": "melee",
|
||||
"player-auto-attack-offhand": "melee",
|
||||
// The core executable catalog keeps these two seeded actions' stable IDs
|
||||
// when their local Spell.dbc rank rows are merged.
|
||||
"hunter-arcane-shot": "ranged",
|
||||
"hunter-steady-shot": "ranged",
|
||||
});
|
||||
|
||||
/**
|
||||
* Classifies only local generated Hunter rows whose descriptions/effects name
|
||||
* a direct melee strike or ranged shot. Trap, sting, and channel rows are not
|
||||
* inferred to be weapon attacks from their damage school or range alone.
|
||||
*/
|
||||
export function wow335HunterGoForTheThroatAttackKindForAbility(
|
||||
abilityId: string,
|
||||
): "melee" | "ranged" | null {
|
||||
return HUNTER_GO_FOR_THE_THROAT_DIRECT_ATTACK_KINDS[abilityId] ?? null;
|
||||
}
|
||||
|
||||
export interface Wow335HunterGoForTheThroatRankMechanics {
|
||||
readonly description: string;
|
||||
readonly focusPerProc: number;
|
||||
readonly internalCooldownMs: number;
|
||||
}
|
||||
|
||||
export interface Wow335HunterGoForTheThroatProcEvent {
|
||||
readonly attackType: "melee" | "ranged" | "other";
|
||||
readonly critical: boolean;
|
||||
readonly hasLivingActivePet: boolean;
|
||||
readonly focusCurrent: number;
|
||||
readonly focusMaximum: number;
|
||||
readonly lastProcAt: number | null;
|
||||
readonly now: number;
|
||||
}
|
||||
|
||||
export interface Wow335HunterGoForTheThroatProcPlan {
|
||||
readonly focusGain: number;
|
||||
readonly focusAfter: number;
|
||||
readonly lastProcAt: number;
|
||||
}
|
||||
|
||||
const talent = runtimeTalentNodeById(WOW335_HUNTER_GO_FOR_THE_THROAT_TALENT_ID);
|
||||
|
||||
/**
|
||||
* Reads the Focus payload from the generated 3.3.5a talent rank text.
|
||||
* The generated ranks say 15/30 Focus and share a two-second proc lockout.
|
||||
*/
|
||||
export function wow335HunterGoForTheThroatRankMechanics(
|
||||
rank: number,
|
||||
): Wow335HunterGoForTheThroatRankMechanics | null {
|
||||
const normalizedRank = Math.trunc(Number.isFinite(rank) ? rank : 0);
|
||||
const description = talent?.rankDescriptions[normalizedRank - 1];
|
||||
if (!description) return null;
|
||||
|
||||
const focusMatch = description.match(/tamed beast to generate (\d+(?:\.\d+)?) Focus/i);
|
||||
const cooldownMatch = description.match(/cannot occur more than once every (\d+(?:\.\d+)?) seconds/i);
|
||||
const focusPerProc = focusMatch ? Number(focusMatch[1]) : Number.NaN;
|
||||
const cooldownSeconds = cooldownMatch ? Number(cooldownMatch[1]) : Number.NaN;
|
||||
if (!Number.isFinite(focusPerProc) || focusPerProc <= 0
|
||||
|| !Number.isFinite(cooldownSeconds) || cooldownSeconds <= 0) return null;
|
||||
|
||||
return Object.freeze({
|
||||
description,
|
||||
focusPerProc,
|
||||
internalCooldownMs: Math.round(cooldownSeconds * 1_000),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Plans one deterministic pet Focus proc after an eligible Hunter crit.
|
||||
* The caller owns pet selection, regeneration, and resource mutation; Focus
|
||||
* is capped to the pet's actual current maximum.
|
||||
*/
|
||||
export function wow335HunterGoForTheThroatProcPlan(
|
||||
rank: number,
|
||||
event: Wow335HunterGoForTheThroatProcEvent,
|
||||
): Wow335HunterGoForTheThroatProcPlan | null {
|
||||
const mechanics = wow335HunterGoForTheThroatRankMechanics(rank);
|
||||
if (!mechanics
|
||||
|| (event.attackType !== "melee" && event.attackType !== "ranged")
|
||||
|| !event.critical
|
||||
|| !event.hasLivingActivePet
|
||||
|| !Number.isFinite(event.now)
|
||||
|| (event.lastProcAt !== null
|
||||
&& (!Number.isFinite(event.lastProcAt)
|
||||
|| event.now - event.lastProcAt < mechanics.internalCooldownMs))) return null;
|
||||
|
||||
const maximum = Math.max(0, Number.isFinite(event.focusMaximum) ? event.focusMaximum : 0);
|
||||
const current = Math.max(0, Math.min(maximum, Number.isFinite(event.focusCurrent) ? event.focusCurrent : 0));
|
||||
const focusAfter = Math.min(maximum, current + mechanics.focusPerProc);
|
||||
return Object.freeze({
|
||||
focusGain: focusAfter - current,
|
||||
focusAfter,
|
||||
lastProcAt: event.now,
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user