Files
healer-man/src/game/bossBalanceOverrides.ts
T
2026-09-20 11:23:13 -04:00

81 lines
3.8 KiB
TypeScript

export interface BossBalanceOverride {
readonly id: string;
readonly dungeonId: string;
readonly entityId: string;
readonly minimumLevel?: number;
readonly maximumLevel?: number;
readonly healthScale?: number;
readonly damageScale?: number;
readonly reason: string;
}
export interface ResolvedBossBalanceOverride {
readonly healthScale: number;
readonly damageScale: number;
readonly matchedOverrideIds: readonly string[];
}
/**
* Evidence-based exceptions to the shared boss curve. Entries may cover one
* level, a bracket, or every level when both bounds are omitted. Multipliers
* compose so a broad boss correction can coexist with a narrower bracket fix.
*/
export const BOSS_BALANCE_OVERRIDES: readonly BossBalanceOverride[] = Object.freeze([
{ id: "level-80-amanitar-pressure", dungeonId: "ahnkahet-old-kingdom", entityId: "entry-30258",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.7,
reason: "Ready-five audit: 100% wipes and 3.14x median incoming pressure." },
{ id: "level-80-warp-splinter-pressure", dungeonId: "botanica", entityId: "entry-17977",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.56,
reason: "Ready-five audit: 100% wipes and 3.90x median incoming pressure." },
{ id: "level-80-maiden-of-grief-pressure", dungeonId: "halls-of-stone", entityId: "entry-27975",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.56,
reason: "Ready-five audit: 100% wipes and 3.93x median incoming pressure." },
{ id: "level-80-ick-pressure", dungeonId: "pit-of-saron", entityId: "entry-36476",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.54,
reason: "Ready-five audit: 100% wipes and 4.11x median incoming pressure." },
{ id: "level-80-murmur-pressure", dungeonId: "shadow-labyrinth", entityId: "entry-18708",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.68,
reason: "Ready-five audit: 100% wipes and 3.22x median incoming pressure." },
{ id: "level-80-keristrasza-pressure", dungeonId: "the-nexus", entityId: "entry-26723",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.7,
reason: "Ready-five audit: 100% wipes and 3.12x median incoming pressure." },
{ id: "level-80-drakos-pressure", dungeonId: "the-oculus", entityId: "entry-27654",
minimumLevel: 80, maximumLevel: 80, damageScale: 0.45,
reason: "Ready-five audit: 100% wipes and 4.89x median incoming pressure." },
{ id: "level-80-black-knight-phase-3-pressure", dungeonId: "trial-of-the-champion",
entityId: "wrath-event-black-knight-phase-3", minimumLevel: 80, maximumLevel: 80, damageScale: 0.5,
reason: "Ready-five audit: 100% wipes and 4.33x median incoming pressure." },
]);
function sourceDungeonId(dungeonId: string, entityId: string): string {
const prefix = "boss-testing:";
const suffix = `:${entityId}`;
return dungeonId.startsWith(prefix) && dungeonId.endsWith(suffix)
? dungeonId.slice(prefix.length, -suffix.length)
: dungeonId;
}
function safeScale(value: number | undefined): number {
return Number.isFinite(value) ? Math.max(0.05, value!) : 1;
}
export function resolveBossBalanceOverride(
dungeonId: string,
entityId: string,
level: number,
overrides: readonly BossBalanceOverride[] = BOSS_BALANCE_OVERRIDES,
): ResolvedBossBalanceOverride {
const sourceId = sourceDungeonId(dungeonId, entityId);
const matching = overrides.filter((entry) => (
entry.dungeonId === sourceId
&& entry.entityId === entityId
&& level >= (entry.minimumLevel ?? Number.NEGATIVE_INFINITY)
&& level <= (entry.maximumLevel ?? Number.POSITIVE_INFINITY)
));
return Object.freeze({
healthScale: matching.reduce((scale, entry) => scale * safeScale(entry.healthScale), 1),
damageScale: matching.reduce((scale, entry) => scale * safeScale(entry.damageScale), 1),
matchedOverrideIds: Object.freeze(matching.map((entry) => entry.id)),
});
}