104 lines
4.2 KiB
TypeScript
104 lines
4.2 KiB
TypeScript
import type { ActiveAura, AuraDefinition, AuraModifier } from "./combatAuras";
|
|
import type { AbilityEffect } from "./abilityCatalog";
|
|
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_SKILL_ID = 490347;
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_AURA_TAG = "rom-energized-hands";
|
|
export const ROM_ROGUE_POISONOUS_HANDS_EXCLUSIVE_GROUP = "rom-rogue-hands";
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_DURATION_MS = 900_000;
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_COOLDOWN_REDUCTION_MS = 100;
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_MAX_ENERGY_PERCENT = 50;
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_REGENERATION_PERCENT = 100;
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_COST_PERCENT = 20;
|
|
|
|
export const ROM_ROGUE_ENERGIZED_HANDS_DESCRIPTION =
|
|
"Increases Energy recovery by 100%, reduces skill cooldowns by 0.1 seconds, and increases maximum Energy by 50%, but increases Energy costs by 20% for 900 seconds. Mutually exclusive with Poisonous Hands.";
|
|
|
|
export const ROM_ROGUE_POISONOUS_HANDS_DESCRIPTION =
|
|
"Uses 1 Poison Bottle. For 900 seconds, regular attacks have a 50% chance to apply Poisonous Hands to the target, reducing damage dealt by the target by 8%. Each successful application stacks up to 10 times and grants you 1% Physical Attack Speed per stack applied to your current target. Attacking a new target resets this attack-speed sequence. Mutually exclusive with Energized Hands.";
|
|
|
|
export function romRogueEnergizedHandsAuraDefinition(): AuraDefinition {
|
|
const modifiers: readonly AuraModifier[] = [
|
|
{
|
|
kind: "resource",
|
|
resource: "energy",
|
|
property: "maximum",
|
|
operation: "percent",
|
|
value: ROM_ROGUE_ENERGIZED_HANDS_MAX_ENERGY_PERCENT / 100,
|
|
},
|
|
{
|
|
kind: "resource",
|
|
resource: "energy",
|
|
property: "regeneration",
|
|
operation: "percent",
|
|
value: ROM_ROGUE_ENERGIZED_HANDS_REGENERATION_PERCENT / 100,
|
|
},
|
|
{
|
|
kind: "resource",
|
|
resource: "energy",
|
|
property: "cost",
|
|
operation: "percent",
|
|
value: ROM_ROGUE_ENERGIZED_HANDS_COST_PERCENT / 100,
|
|
},
|
|
];
|
|
return {
|
|
id: "rom-rogue-energized-hands",
|
|
name: "Energized Hands",
|
|
description: ROM_ROGUE_ENERGIZED_HANDS_DESCRIPTION,
|
|
disposition: "buff",
|
|
durationMs: ROM_ROGUE_ENERGIZED_HANDS_DURATION_MS,
|
|
exclusiveGroup: ROM_ROGUE_POISONOUS_HANDS_EXCLUSIVE_GROUP,
|
|
utilityTags: [ROM_ROGUE_ENERGIZED_HANDS_AURA_TAG],
|
|
modifiers,
|
|
};
|
|
}
|
|
|
|
export function romRogueEnergizedHandsApplyEffect(): AbilityEffect {
|
|
return {
|
|
kind: "apply-aura",
|
|
recipient: "caster",
|
|
sourceEffectType: 6,
|
|
sourceAuraType: 0,
|
|
miscValue: 0,
|
|
triggerSpellId: 0,
|
|
aura: romRogueEnergizedHandsAuraDefinition(),
|
|
};
|
|
}
|
|
|
|
export function romRogueEnergizedHandsActive(
|
|
auras: readonly ActiveAura[],
|
|
actorId: string,
|
|
now?: number,
|
|
): boolean {
|
|
return auras.some((aura) => aura.targetId === actorId
|
|
&& aura.definition.utilityTags?.includes(ROM_ROGUE_ENERGIZED_HANDS_AURA_TAG)
|
|
&& (now === undefined || aura.expiresAt === null || aura.expiresAt > now));
|
|
}
|
|
|
|
/**
|
|
* Translate the current max-Energy aura between snapshots without changing
|
|
* current Energy. The stored pool maximum is the runtime authority, so apply
|
|
* only the ratio of the old and new aura modifiers (refreshes remain neutral).
|
|
*/
|
|
export function romRogueEnergyMaximumAfterAuraTransition(
|
|
currentMaximum: number,
|
|
previousAuras: readonly ActiveAura[],
|
|
nextAuras: readonly ActiveAura[],
|
|
targetId: string,
|
|
): number {
|
|
const maximumModifier = (auras: readonly ActiveAura[]) => auras
|
|
.filter((aura) => aura.targetId === targetId)
|
|
.reduce((total, aura) => {
|
|
const hasEnergizedHands = aura.definition.utilityTags?.includes(ROM_ROGUE_ENERGIZED_HANDS_AURA_TAG);
|
|
if (!hasEnergizedHands) return total;
|
|
const percent = (aura.definition.modifiers ?? []).reduce((value, modifier) => (
|
|
modifier.kind === "resource" && modifier.resource === "energy" && modifier.property === "maximum"
|
|
? value + (modifier.operation === "percent" ? modifier.value : 0)
|
|
: value
|
|
), 0);
|
|
return total * (1 + percent);
|
|
}, 1);
|
|
const previous = maximumModifier(previousAuras);
|
|
const next = maximumModifier(nextAuras);
|
|
return Math.max(0, Math.round(currentMaximum * next / Math.max(0.0001, previous)));
|
|
}
|