forked from phenom/healer-man
Update class mechanics, stat rulesets, and CoA reference catalogs
This commit is contained in:
1 parent
b16821e92e
commit
39458d5de6
221 files changed
+2584962
-568
No files matched your search
@@ -0,0 +1,108 @@
|
||||
import { WOW335_TALENT_NODES } from "./wow335Talents.generated";
|
||||
|
||||
export const WOW335_DEATH_KNIGHT_WANDERING_PLAGUE_TALENT_ID = "talent-2003";
|
||||
export const WOW335_DEATH_KNIGHT_WANDERING_PLAGUE_DAMAGE_ABILITY_ID = "wow335-death-knight-wandering-plague";
|
||||
export const WOW335_DEATH_KNIGHT_WANDERING_PLAGUE_DISEASE_TAGS = [
|
||||
"blood-plague",
|
||||
"frost-fever",
|
||||
] as const;
|
||||
|
||||
const WANDERING_PLAGUE_TARGET_RADIUS_YARDS = 8;
|
||||
const WANDERING_PLAGUE_INTERNAL_COOLDOWN_MS = 1_000;
|
||||
const WANDERING_PLAGUE_TALENT = WOW335_TALENT_NODES.find(
|
||||
(node) => node.id === WOW335_DEATH_KNIGHT_WANDERING_PLAGUE_TALENT_ID,
|
||||
);
|
||||
const DEATH_KNIGHT_GLOBAL_MELEE_CRIT_TALENT_SPECS = [
|
||||
{
|
||||
talentId: "talent-1943",
|
||||
rankDescriptionPattern: /chance to critically hit with weapons, spells and abilities by (\d+(?:\.\d+)?)%/i,
|
||||
},
|
||||
{
|
||||
talentId: "talent-2043",
|
||||
rankDescriptionPattern: /critical strike chance with weapons and spells by (\d+(?:\.\d+)?)% at all times/i,
|
||||
},
|
||||
] as const;
|
||||
const WANDERING_PLAGUE_DISEASE_TAG_SET = new Set<string>(WOW335_DEATH_KNIGHT_WANDERING_PLAGUE_DISEASE_TAGS);
|
||||
|
||||
export interface Wow335DeathKnightWanderingPlagueProfile {
|
||||
readonly rank: number;
|
||||
readonly extraDamagePercent: number;
|
||||
readonly targetRadiusYards: number;
|
||||
readonly internalCooldownMs: number;
|
||||
}
|
||||
|
||||
export interface Wow335DeathKnightWanderingPlagueProc {
|
||||
readonly extraDamage: number;
|
||||
readonly targetRadiusYards: number;
|
||||
readonly nextReadyAt: number;
|
||||
}
|
||||
|
||||
/** Read the splash amount from the local generated 3.3.5a talent rank. */
|
||||
export function wow335DeathKnightWanderingPlagueProfileForRank(
|
||||
rank: number,
|
||||
): Wow335DeathKnightWanderingPlagueProfile | null {
|
||||
const normalizedRank = Math.max(0, Math.min(3, Math.trunc(rank)));
|
||||
if (normalizedRank <= 0) return null;
|
||||
const effect = WANDERING_PLAGUE_TALENT?.rankEffects[normalizedRank - 1]?.effects.find(
|
||||
(candidate) => candidate.auraType === 4,
|
||||
);
|
||||
const extraDamagePercent = Math.max(0, Math.trunc(effect?.basePoints ?? 0));
|
||||
if (extraDamagePercent <= 0) return null;
|
||||
return {
|
||||
rank: normalizedRank,
|
||||
extraDamagePercent,
|
||||
targetRadiusYards: WANDERING_PLAGUE_TARGET_RADIUS_YARDS,
|
||||
internalCooldownMs: WANDERING_PLAGUE_INTERNAL_COOLDOWN_MS,
|
||||
};
|
||||
}
|
||||
|
||||
export function wow335DeathKnightWanderingPlagueIsDiseaseTag(tag: string | undefined): boolean {
|
||||
return tag !== undefined && WANDERING_PLAGUE_DISEASE_TAG_SET.has(tag);
|
||||
}
|
||||
|
||||
/** Read the two generated global melee-crit passives for the live proc chance. */
|
||||
export function wow335DeathKnightWanderingPlagueMeleeCritBonusPercentForTalentRanks(
|
||||
talentRanks: Readonly<Record<string, number>>,
|
||||
): number {
|
||||
return DEATH_KNIGHT_GLOBAL_MELEE_CRIT_TALENT_SPECS.reduce((total, spec) => {
|
||||
const node = WOW335_TALENT_NODES.find((candidate) => candidate.id === spec.talentId);
|
||||
const rank = Math.max(0, Math.min(node?.rankDescriptions.length ?? 0,
|
||||
Math.trunc(talentRanks[spec.talentId] ?? 0)));
|
||||
if (rank <= 0) return total;
|
||||
const description = node?.rankDescriptions[rank - 1] ?? "";
|
||||
const match = description.match(spec.rankDescriptionPattern);
|
||||
return total + (match ? Number(match[1]) : 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/** Resolve a qualifying disease tick against the source's live melee critical chance. */
|
||||
export function wow335DeathKnightWanderingPlagueProcForDiseaseTick(
|
||||
rank: number,
|
||||
tag: string | undefined,
|
||||
tickDamage: number,
|
||||
meleeCriticalChancePercent: number,
|
||||
roll: number,
|
||||
now: number,
|
||||
readyAt: number,
|
||||
): Wow335DeathKnightWanderingPlagueProc | null {
|
||||
const profile = wow335DeathKnightWanderingPlagueProfileForRank(rank);
|
||||
if (!profile
|
||||
|| !wow335DeathKnightWanderingPlagueIsDiseaseTag(tag)
|
||||
|| !Number.isFinite(tickDamage)
|
||||
|| tickDamage <= 0
|
||||
|| !Number.isFinite(meleeCriticalChancePercent)
|
||||
|| meleeCriticalChancePercent <= 0
|
||||
|| !Number.isFinite(roll)
|
||||
|| roll < 0
|
||||
|| roll >= Math.min(100, meleeCriticalChancePercent) / 100
|
||||
|| !Number.isFinite(now)
|
||||
|| !Number.isFinite(readyAt)
|
||||
|| now < readyAt) return null;
|
||||
|
||||
return {
|
||||
// Damage is integer-valued in this runtime; round the source percentage once.
|
||||
extraDamage: Math.max(0, Math.round(tickDamage * profile.extraDamagePercent / 100)),
|
||||
targetRadiusYards: profile.targetRadiusYards,
|
||||
nextReadyAt: now + profile.internalCooldownMs,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user