66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
export const WOW335_WARLOCK_SOUL_LINK_TAG = "wow335-warlock-soul-link";
|
|
|
|
const WOW335_SOUL_LINKABLE_DEMON_SPELLS = new Set([
|
|
"wow335-warlock-summon-voidwalker",
|
|
"wow335-warlock-summon-succubus",
|
|
"wow335-warlock-summon-felhunter",
|
|
"wow335-warlock-summon-felguard",
|
|
"wow335-warlock-inferno",
|
|
]);
|
|
|
|
export interface WarlockSoulLinkSummon {
|
|
readonly sourceAbilityId: string;
|
|
readonly ownerActorId: string;
|
|
readonly health: number;
|
|
readonly expiresAt: number;
|
|
readonly controlledUntil: number;
|
|
readonly position: readonly [number, number, number];
|
|
}
|
|
|
|
/** Soul Link is 6%, plus Demonic Embrace's local rank tooltip of 2/4/6%. */
|
|
export function warlockSoulLinkSharePercent(
|
|
soulLinkRank: number,
|
|
demonicEmbraceRank: number,
|
|
): number {
|
|
const linkRank = Math.max(0, Math.min(1, Math.trunc(Number.isFinite(soulLinkRank) ? soulLinkRank : 0)));
|
|
if (linkRank === 0) return 0;
|
|
const embraceRank = Math.max(0, Math.min(3, Math.trunc(Number.isFinite(demonicEmbraceRank) ? demonicEmbraceRank : 0)));
|
|
return 6 + 2 * embraceRank;
|
|
}
|
|
|
|
/**
|
|
* Selects the caster's live, controlled Warlock demon within the generated
|
|
* talent tooltip's 30-yard link distance. The simulator represents yards as
|
|
* ten combat-position units.
|
|
*/
|
|
export function warlockSoulLinkDemon<T extends WarlockSoulLinkSummon>(
|
|
summons: readonly T[],
|
|
ownerActorId: string,
|
|
ownerPosition: readonly [number, number, number],
|
|
now: number,
|
|
): T | null {
|
|
return summons.find((summon) => (
|
|
WOW335_SOUL_LINKABLE_DEMON_SPELLS.has(summon.sourceAbilityId)
|
|
&& summon.ownerActorId === ownerActorId
|
|
&& summon.health > 0
|
|
&& summon.expiresAt > now
|
|
&& summon.controlledUntil <= now
|
|
&& Math.hypot(
|
|
summon.position[0] - ownerPosition[0],
|
|
summon.position[1] - ownerPosition[1],
|
|
summon.position[2] - ownerPosition[2],
|
|
) <= 300
|
|
)) ?? null;
|
|
}
|
|
|
|
/** Split post-mitigation incoming damage; the transferred portion bypasses prevention. */
|
|
export function warlockSoulLinkDamageSplit(amount: number, sharePercent: number): {
|
|
readonly casterDamage: number;
|
|
readonly demonDamage: number;
|
|
} {
|
|
const safeAmount = Math.max(0, Math.trunc(Number.isFinite(amount) ? amount : 0));
|
|
const safePercent = Math.max(0, Math.min(100, Number.isFinite(sharePercent) ? sharePercent : 0));
|
|
const demonDamage = Math.min(safeAmount, Math.round(safeAmount * safePercent / 100));
|
|
return { casterDamage: safeAmount - demonDamage, demonDamage };
|
|
}
|