forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
1fedb9368d
commit
cc3b2fa4d5
27 files changed
+1988
-83
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import type { ActiveAura, AuraDefinition } from "./combatAuras";
|
||||
|
||||
export const ROGUE_INSTANT_POISON_TAG = "rogue-instant-poison";
|
||||
export const ROGUE_DEADLY_POISON_COATING_TAG = "rogue-deadly-poison-coating";
|
||||
export const ROGUE_DEADLY_POISON_DOSE_TAG = "rogue-deadly-poison-dose";
|
||||
export const ROGUE_ENVENOM_BUFF_TAG = "rogue-envenom-poison-buff";
|
||||
|
||||
export const ROGUE_DEADLY_POISON_AURA: AuraDefinition = Object.freeze({
|
||||
id: "rogue-deadly-poison-doses",
|
||||
name: "Deadly Poison",
|
||||
description: "Deals Nature damage every 3 sec. Stacks up to 5 times.",
|
||||
disposition: "debuff",
|
||||
durationMs: 12_000,
|
||||
maxStacks: 5,
|
||||
stackBehavior: "add",
|
||||
uniqueBySource: true,
|
||||
utilityTags: [ROGUE_DEADLY_POISON_DOSE_TAG],
|
||||
dispelCategory: "poison",
|
||||
});
|
||||
|
||||
export interface RoguePoisonFinisherScaling {
|
||||
readonly doseAuraTag: string;
|
||||
readonly maximumDoses: number;
|
||||
readonly basePointsPerDose: number;
|
||||
readonly attackPowerCoefficientPerDose: number;
|
||||
}
|
||||
|
||||
export interface RoguePoisonApplicationChances {
|
||||
readonly instant: number;
|
||||
readonly deadly: number;
|
||||
}
|
||||
|
||||
const MASTER_POISONER_TALENT_ID = "talent-1715";
|
||||
|
||||
const INSTANT_POISON_BASE_BY_LEVEL: readonly (readonly [level: number, amount: number])[] = [
|
||||
[20, 22], [28, 43], [36, 79], [44, 121], [52, 170], [60, 225], [68, 284], [73, 350],
|
||||
];
|
||||
|
||||
const DEADLY_POISON_TICK_BY_LEVEL: readonly (readonly [level: number, amount: number])[] = [
|
||||
[30, 12], [38, 20], [46, 28], [54, 36], [60, 44], [62, 52], [70, 62], [76, 74],
|
||||
];
|
||||
|
||||
function rankAmount(level: number, ranks: readonly (readonly [level: number, amount: number])[]): number {
|
||||
let amount = ranks[0]?.[1] ?? 1;
|
||||
for (const [requiredLevel, rankAmountValue] of ranks) {
|
||||
if (level < requiredLevel) break;
|
||||
amount = rankAmountValue;
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
export function rogueInstantPoisonDamage(level: number, attackPower: number): number {
|
||||
return Math.max(1, Math.round(rankAmount(level, INSTANT_POISON_BASE_BY_LEVEL) + Math.max(0, attackPower) * 0.1));
|
||||
}
|
||||
|
||||
export function rogueDeadlyPoisonTickDamage(level: number, attackPower: number, stacks: number): number {
|
||||
const boundedStacks = Math.max(0, Math.min(5, Math.floor(stacks)));
|
||||
return Math.max(0, Math.round(
|
||||
(rankAmount(level, DEADLY_POISON_TICK_BY_LEVEL) + Math.max(0, attackPower) * 0.03) * boundedStacks,
|
||||
));
|
||||
}
|
||||
|
||||
export function roguePoisonApplicationChances(envenomBuffActive: boolean): RoguePoisonApplicationChances {
|
||||
return Object.freeze({
|
||||
// Envenom increases Instant Poison's application frequency by 75% and
|
||||
// adds 15 percentage points to Deadly Poison's application chance.
|
||||
instant: 0.2 * (envenomBuffActive ? 1.75 : 1),
|
||||
deadly: 0.3 + (envenomBuffActive ? 0.15 : 0),
|
||||
});
|
||||
}
|
||||
|
||||
export function rogueEnvenomPreservationChance(
|
||||
talentRanks: Readonly<Record<string, number>>,
|
||||
): number {
|
||||
const rank = Math.max(0, Math.min(3, Math.floor(talentRanks[MASTER_POISONER_TALENT_ID] ?? 0)));
|
||||
return [0, 0.33, 0.66, 1][rank] ?? 0;
|
||||
}
|
||||
|
||||
function stableRogueUnit(seed: string): number {
|
||||
let hash = 2166136261;
|
||||
for (let index = 0; index < seed.length; index += 1) {
|
||||
hash ^= seed.charCodeAt(index);
|
||||
hash = Math.imul(hash, 16777619);
|
||||
}
|
||||
return (hash >>> 0) / 0x1_0000_0000;
|
||||
}
|
||||
|
||||
export function rogueEnvenomPreservesDeadlyPoison(
|
||||
talentRanks: Readonly<Record<string, number>>,
|
||||
seed: string,
|
||||
): boolean {
|
||||
const chance = rogueEnvenomPreservationChance(talentRanks);
|
||||
return chance >= 1 || (chance > 0 && stableRogueUnit(seed) < chance);
|
||||
}
|
||||
|
||||
export function matchingAuraStacks(
|
||||
auras: readonly ActiveAura[],
|
||||
tag: string,
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
now: number,
|
||||
): number {
|
||||
return auras.find((aura) => (
|
||||
aura.sourceId === sourceId
|
||||
&& aura.targetId === targetId
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now)
|
||||
&& aura.definition.utilityTags?.includes(tag)
|
||||
))?.stacks ?? 0;
|
||||
}
|
||||
|
||||
export function hasActiveAuraTag(
|
||||
auras: readonly ActiveAura[],
|
||||
tag: string,
|
||||
targetId: string,
|
||||
now: number,
|
||||
): boolean {
|
||||
return auras.some((aura) => (
|
||||
aura.targetId === targetId
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now)
|
||||
&& aura.definition.utilityTags?.includes(tag)
|
||||
));
|
||||
}
|
||||
Reference in new issue
Block a user