Update Healer Man server

This commit is contained in:
phenom committed 2026-10-06 22:25:34 -04:00
1 parent 6114a2295f
commit 69809f7228
593 files changed
+903643 -40774

No files matched your search

+45 -5
View File
@@ -4,7 +4,7 @@ import type { CharacterStats, WotlkAttackKind } from "./wotlkStats";
export type CombatAttackKind = WotlkAttackKind;
export type CombatOutcome = "hit" | "crit" | "miss" | "dodge" | "parry" | "block" | "resist";
export type CombatResistanceMode = "none" | "partial" | "binary";
export type CombatRollStage = "outcome" | "resistance";
export type CombatRollStage = "outcome" | "resistance" | "block" | "critical-block";
/** A roll must be in [0, 1). Values outside that range are safely clamped. */
export type CombatRollSource = (stage: CombatRollStage) => number;
@@ -26,6 +26,8 @@ export interface CombatDefenderStats {
readonly parryPercent?: number;
readonly blockPercent?: number;
readonly blockValue?: number;
/** Integer chance passed to AzerothCore's roll_chance_i after a successful block. */
readonly criticalBlockChancePercent?: number;
readonly criticalAvoidancePercent?: number;
readonly damageReductionPercent?: number;
readonly resistances?: Partial<Readonly<Record<DamageSchool, number>>>;
@@ -47,6 +49,8 @@ export interface CombatResultRules {
readonly canDodge?: boolean;
readonly canParry?: boolean;
readonly canBlock?: boolean;
/** Source direct physical spells roll block separately after landing, including crits. */
readonly blockMode?: "post-hit";
/** Source mechanic exception (for example, bleed effects that bypass armor). */
readonly ignoreArmor?: boolean;
readonly resistanceMode?: CombatResistanceMode;
@@ -98,6 +102,12 @@ export interface CombatResult {
readonly outcomeRoll: number;
readonly resistanceRoll: number | null;
readonly resistanceFraction: number;
readonly postHitBlockRoll?: number;
readonly postHitBlockChancePercent?: number;
/** Integer in [0, 99], matching the sample consumed by roll_chance_i. */
readonly criticalBlockRoll?: number;
readonly criticalBlockChancePercent?: number;
readonly criticalBlocked?: boolean;
}
const DEFAULT_CRITICAL_DAMAGE_MULTIPLIER = 2;
@@ -180,7 +190,7 @@ export function combatAttackTable(input: ResolveCombatResultInput): CombatAttack
(rules.canParry ?? defaultCanParry)
? Math.max(0, finitePercent(defender.parryPercent) - expertise)
: 0,
(rules.canBlock ?? defaultCanBlock) ? finitePercent(defender.blockPercent) : 0,
rules.blockMode !== "post-hit" && (rules.canBlock ?? defaultCanBlock) ? finitePercent(defender.blockPercent) : 0,
rules.canCrit === false
? 0
: Math.max(
@@ -319,9 +329,33 @@ export function resolveCombatResult(input: ResolveCombatResultInput): CombatResu
);
let remaining = damageBeforeMitigation - armorMitigatedAmount;
const blocked = rolledOutcome === "block";
const postHitBlockChancePercent = input.rules?.blockMode === "post-hit"
&& input.rules.canBlock !== false && input.damageSchool === "physical"
&& (input.attackKind === "melee" || input.attackKind === "ranged")
? finitePercent(input.defender.blockPercent) : 0;
const postHitBlockRoll = postHitBlockChancePercent > 0
? normalizedRoll(rollSource("block")) : undefined;
const blocked = rolledOutcome === "block"
|| (postHitBlockRoll !== undefined && postHitBlockRoll * 100 < postHitBlockChancePercent);
// AzerothCore's roll_chance_i is chance > irand(0, 99), so even 0% and
// 100% consume a roll when the source supplied this stat. Keep the optional
// field absent as the compatibility boundary for existing callers.
const hasCriticalBlockChance = input.defender.criticalBlockChancePercent !== undefined;
const criticalBlockChancePercent = hasCriticalBlockChance
? (Number.isFinite(input.defender.criticalBlockChancePercent)
? Math.trunc(Number(input.defender.criticalBlockChancePercent))
: 0)
: undefined;
const criticalBlockRoll = blocked && hasCriticalBlockChance
? Math.floor(normalizedRoll(rollSource("critical-block")) * 100)
: undefined;
const criticalBlocked = criticalBlockRoll !== undefined
&& criticalBlockChancePercent !== undefined
&& criticalBlockChancePercent > criticalBlockRoll;
const blockedAmount = blocked
? Math.min(remaining, Math.round(finiteNonNegative(input.defender.blockValue)))
? Math.min(remaining, Math.round(
finiteNonNegative(input.defender.blockValue) * (criticalBlocked ? 2 : 1),
))
: 0;
remaining -= blockedAmount;
@@ -359,7 +393,7 @@ export function resolveCombatResult(input: ResolveCombatResultInput): CombatResu
remaining -= damageReductionAmount;
return Object.freeze({
outcome: rolledOutcome,
outcome: blocked && !critical ? "block" : rolledOutcome,
landed: true,
critical,
missed: false,
@@ -382,6 +416,12 @@ export function resolveCombatResult(input: ResolveCombatResultInput): CombatResu
outcomeRoll,
resistanceRoll,
resistanceFraction: resistedFraction,
...(postHitBlockRoll !== undefined ? { postHitBlockRoll, postHitBlockChancePercent } : {}),
...(criticalBlockChancePercent !== undefined && criticalBlockRoll !== undefined ? {
criticalBlockChancePercent,
criticalBlocked,
criticalBlockRoll,
} : {}),
});
}