Update Healer Man server
This commit is contained in:
1 parent
6114a2295f
commit
69809f7228
593 files changed
+903643
-40774
No files matched your search
@@ -0,0 +1,258 @@
|
||||
import {
|
||||
creatureTemplateBaseDamage,
|
||||
creatureTemplateReference,
|
||||
} from "./azerothCoreReference";
|
||||
|
||||
/**
|
||||
* Source-backed periodic damage used by Stockade Rend. Values describe the
|
||||
* 3.3.5 Spell.dbc source rows before AzerothCore's caster-level scaling,
|
||||
* spell damage modifiers, and target mitigation are applied.
|
||||
*/
|
||||
export type EnemyPeriodicDamageSourceProfile = "ascension-patch-d" | "epoch-client";
|
||||
|
||||
export interface EnemyPeriodicDamageSource {
|
||||
readonly spellId: 6547 | 16509;
|
||||
readonly sourceProfile: EnemyPeriodicDamageSourceProfile;
|
||||
readonly durationMs: 15_000;
|
||||
readonly tickIntervalMs: 3_000;
|
||||
readonly tickCount: 5;
|
||||
readonly school: "physical";
|
||||
readonly schoolMask: 1;
|
||||
/** Spell.dbc SpellDamageClass: 2 (melee). */
|
||||
readonly damageClass: 2;
|
||||
readonly attackKind: "melee";
|
||||
/** Spell.dbc effect opcode and aura type: APPLY_AURA / PERIODIC_DAMAGE. */
|
||||
readonly effectType: 6;
|
||||
readonly auraType: 3;
|
||||
/** Spell.dbc MECHANIC_BLEED. */
|
||||
readonly mechanic: 15;
|
||||
/** Spell.dbc implicit target A 6, with no secondary target. */
|
||||
readonly implicitTargetA: 6;
|
||||
readonly implicitTargetB: 0;
|
||||
readonly durationIndex: 8;
|
||||
readonly stackAmount: 0;
|
||||
readonly effectBasePoints: number;
|
||||
readonly effectDieSides: 1;
|
||||
readonly effectRealPointsPerLevel: 0;
|
||||
readonly spellLevel: 20;
|
||||
readonly baseLevel: 20;
|
||||
readonly maxLevel: 0;
|
||||
readonly scalesWithCreatureLevel: boolean;
|
||||
/**
|
||||
* The core CalcValue result before creature-level scaling and damage
|
||||
* modifiers: BasePoints plus a roll from 1 through DieSides.
|
||||
*/
|
||||
readonly dbcBaseTickAmount: number;
|
||||
/** Periodic bleed damage bypasses armor in the referenced core rules. */
|
||||
readonly periodicIgnoresArmor: true;
|
||||
/** Ordinary same-caster casts refresh duration/ticks and restart the tick phase. */
|
||||
readonly sameCasterRefresh: "duration-and-tick-count-reset-phase";
|
||||
/** TRIGGERED_NO_PERIODIC_RESET casts refresh duration/ticks but keep the old phase. */
|
||||
readonly triggeredNoPeriodicResetRefresh: "duration-and-tick-count-preserve-phase";
|
||||
/** Independent periodic auras from different casters can coexist. */
|
||||
readonly differentCasterStacking: "independent";
|
||||
}
|
||||
|
||||
export type EnemyPeriodicDamageTickAmountResult =
|
||||
| {
|
||||
readonly status: "resolved";
|
||||
/** Source core SpellEffectInfo::CalcValue result, before spell/target modifiers. */
|
||||
readonly tickAmount: number;
|
||||
readonly dbcBaseTickAmount: number;
|
||||
readonly creatureLevelScale: number;
|
||||
}
|
||||
| {
|
||||
readonly status: "unsupported";
|
||||
readonly reason: "invalid-creature-level" | "creature-template-missing" | "creature-base-damage-missing";
|
||||
};
|
||||
|
||||
const STOCKADE_REND_SOURCE: Readonly<Record<EnemyPeriodicDamageSourceProfile, Readonly<
|
||||
Partial<Record<EnemyPeriodicDamageSource["spellId"], EnemyPeriodicDamageSource>>
|
||||
>>> = Object.freeze({
|
||||
"ascension-patch-d": Object.freeze({
|
||||
6547: Object.freeze({
|
||||
spellId: 6547,
|
||||
sourceProfile: "ascension-patch-d",
|
||||
durationMs: 15_000,
|
||||
tickIntervalMs: 3_000,
|
||||
tickCount: 5,
|
||||
school: "physical",
|
||||
schoolMask: 1,
|
||||
damageClass: 2,
|
||||
attackKind: "melee",
|
||||
effectType: 6,
|
||||
auraType: 3,
|
||||
mechanic: 15,
|
||||
implicitTargetA: 6,
|
||||
implicitTargetB: 0,
|
||||
durationIndex: 8,
|
||||
stackAmount: 0,
|
||||
effectBasePoints: 13,
|
||||
effectDieSides: 1,
|
||||
effectRealPointsPerLevel: 0,
|
||||
spellLevel: 20,
|
||||
baseLevel: 20,
|
||||
maxLevel: 0,
|
||||
scalesWithCreatureLevel: false,
|
||||
dbcBaseTickAmount: 14,
|
||||
periodicIgnoresArmor: true,
|
||||
sameCasterRefresh: "duration-and-tick-count-reset-phase",
|
||||
triggeredNoPeriodicResetRefresh: "duration-and-tick-count-preserve-phase",
|
||||
differentCasterStacking: "independent",
|
||||
}),
|
||||
16509: Object.freeze({
|
||||
spellId: 16509,
|
||||
sourceProfile: "ascension-patch-d",
|
||||
durationMs: 15_000,
|
||||
tickIntervalMs: 3_000,
|
||||
tickCount: 5,
|
||||
school: "physical",
|
||||
schoolMask: 1,
|
||||
damageClass: 2,
|
||||
attackKind: "melee",
|
||||
effectType: 6,
|
||||
auraType: 3,
|
||||
mechanic: 15,
|
||||
implicitTargetA: 6,
|
||||
implicitTargetB: 0,
|
||||
durationIndex: 8,
|
||||
stackAmount: 0,
|
||||
effectBasePoints: 26,
|
||||
effectDieSides: 1,
|
||||
effectRealPointsPerLevel: 0,
|
||||
spellLevel: 20,
|
||||
baseLevel: 20,
|
||||
maxLevel: 0,
|
||||
scalesWithCreatureLevel: true,
|
||||
dbcBaseTickAmount: 27,
|
||||
periodicIgnoresArmor: true,
|
||||
sameCasterRefresh: "duration-and-tick-count-reset-phase",
|
||||
triggeredNoPeriodicResetRefresh: "duration-and-tick-count-preserve-phase",
|
||||
differentCasterStacking: "independent",
|
||||
}),
|
||||
}),
|
||||
"epoch-client": Object.freeze({
|
||||
6547: Object.freeze({
|
||||
spellId: 6547,
|
||||
sourceProfile: "epoch-client",
|
||||
durationMs: 15_000,
|
||||
tickIntervalMs: 3_000,
|
||||
tickCount: 5,
|
||||
school: "physical",
|
||||
schoolMask: 1,
|
||||
damageClass: 2,
|
||||
attackKind: "melee",
|
||||
effectType: 6,
|
||||
auraType: 3,
|
||||
mechanic: 15,
|
||||
implicitTargetA: 6,
|
||||
implicitTargetB: 0,
|
||||
durationIndex: 8,
|
||||
stackAmount: 0,
|
||||
effectBasePoints: 9,
|
||||
effectDieSides: 1,
|
||||
effectRealPointsPerLevel: 0,
|
||||
spellLevel: 20,
|
||||
baseLevel: 20,
|
||||
maxLevel: 0,
|
||||
scalesWithCreatureLevel: false,
|
||||
dbcBaseTickAmount: 10,
|
||||
periodicIgnoresArmor: true,
|
||||
sameCasterRefresh: "duration-and-tick-count-reset-phase",
|
||||
triggeredNoPeriodicResetRefresh: "duration-and-tick-count-preserve-phase",
|
||||
differentCasterStacking: "independent",
|
||||
}),
|
||||
16509: Object.freeze({
|
||||
spellId: 16509,
|
||||
sourceProfile: "epoch-client",
|
||||
durationMs: 15_000,
|
||||
tickIntervalMs: 3_000,
|
||||
tickCount: 5,
|
||||
school: "physical",
|
||||
schoolMask: 1,
|
||||
damageClass: 2,
|
||||
attackKind: "melee",
|
||||
effectType: 6,
|
||||
auraType: 3,
|
||||
mechanic: 15,
|
||||
implicitTargetA: 6,
|
||||
implicitTargetB: 0,
|
||||
durationIndex: 8,
|
||||
stackAmount: 0,
|
||||
effectBasePoints: 26,
|
||||
effectDieSides: 1,
|
||||
effectRealPointsPerLevel: 0,
|
||||
spellLevel: 20,
|
||||
baseLevel: 20,
|
||||
maxLevel: 0,
|
||||
scalesWithCreatureLevel: true,
|
||||
dbcBaseTickAmount: 27,
|
||||
periodicIgnoresArmor: true,
|
||||
sameCasterRefresh: "duration-and-tick-count-reset-phase",
|
||||
triggeredNoPeriodicResetRefresh: "duration-and-tick-count-preserve-phase",
|
||||
differentCasterStacking: "independent",
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
/** Returns an audited Rend source row, or undefined for an unverified spell/profile pair. */
|
||||
export function enemyPeriodicDamageSource(
|
||||
spellId: number,
|
||||
sourceProfile: EnemyPeriodicDamageSourceProfile,
|
||||
): EnemyPeriodicDamageSource | undefined {
|
||||
if (spellId !== 6547 && spellId !== 16509) return undefined;
|
||||
return STOCKADE_REND_SOURCE[sourceProfile][spellId];
|
||||
}
|
||||
|
||||
/** Initial application ticks at one full source amplitude after the aura is applied. */
|
||||
export function enemyPeriodicTickTimes(
|
||||
source: EnemyPeriodicDamageSource,
|
||||
appliedAtMs: number,
|
||||
): number[] {
|
||||
return Array.from({ length: source.tickCount }, (_, index) => (
|
||||
appliedAtMs + (index + 1) * source.tickIntervalMs
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the NPC-side SpellEffectInfo::CalcValue amount for these audited
|
||||
* Rend rows. For 16509, this mirrors the referenced core's creature base
|
||||
* damage ratio and final int32 truncation. Spell damage bonuses and target
|
||||
* modifiers are applied later by the periodic damage resolver.
|
||||
*/
|
||||
export function enemyPeriodicDamageTickAmount(
|
||||
serverEntry: number,
|
||||
creatureLevel: number,
|
||||
source: EnemyPeriodicDamageSource,
|
||||
): EnemyPeriodicDamageTickAmountResult {
|
||||
if (!Number.isInteger(creatureLevel) || creatureLevel < 1 || creatureLevel > 80) {
|
||||
return { status: "unsupported", reason: "invalid-creature-level" };
|
||||
}
|
||||
|
||||
if (!source.scalesWithCreatureLevel || creatureLevel === source.spellLevel) {
|
||||
return {
|
||||
status: "resolved",
|
||||
tickAmount: source.dbcBaseTickAmount,
|
||||
dbcBaseTickAmount: source.dbcBaseTickAmount,
|
||||
creatureLevelScale: 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (!creatureTemplateReference(serverEntry)) {
|
||||
return { status: "unsupported", reason: "creature-template-missing" };
|
||||
}
|
||||
|
||||
const creatureStats = creatureTemplateBaseDamage(serverEntry, creatureLevel);
|
||||
const spellLevelStats = creatureTemplateBaseDamage(serverEntry, source.spellLevel);
|
||||
if (!creatureStats || !spellLevelStats) {
|
||||
return { status: "unsupported", reason: "creature-base-damage-missing" };
|
||||
}
|
||||
|
||||
const creatureLevelScale = creatureStats.baseDamage / spellLevelStats.baseDamage;
|
||||
return {
|
||||
status: "resolved",
|
||||
tickAmount: Math.trunc(source.dbcBaseTickAmount * creatureLevelScale),
|
||||
dbcBaseTickAmount: source.dbcBaseTickAmount,
|
||||
creatureLevelScale,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user