Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -0,0 +1,125 @@
|
||||
import { applyAura, type ActiveAura, type AuraDefinition, type CombatEntityId } from "./combatAuras";
|
||||
|
||||
/** Current CoA Druid/Mage Inspiration stack behavior. */
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_PASSIVE_ID = 494322;
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_MAX_STACKS = 10;
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_BONUS_PER_STACK_PERCENT = 2;
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_DURATION_MS = 10_000;
|
||||
/** Current CoA skill page's `Range: 50`, converted with the project's 10:1 CoA range convention. */
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_RADIUS = 5;
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_AURA_TAG = "rom-druid-mage-inspiration";
|
||||
export const ROM_DRUID_MAGE_INSPIRATION_DESCRIPTION =
|
||||
"Passive: whenever you spend Nature's Power, nearby party members gain one Inspiration stack, increasing Physical Attack Speed and Casting Speed by 2% per stack, up to 10 stacks for 10 seconds. CoA also describes a Fireball travel-speed and Nature's Power rider, but current CoA Fireball 499943 cannot be proven identical to local Fireball 490204, so those clauses are not implemented.";
|
||||
|
||||
export type RomDruidMageInspirationPosition = readonly [number, number, number];
|
||||
|
||||
export interface RomDruidMageInspirationRecipient {
|
||||
readonly id: CombatEntityId;
|
||||
readonly position: RomDruidMageInspirationPosition | null;
|
||||
readonly living: boolean;
|
||||
}
|
||||
|
||||
/** Select modeled living party actors inside the interpreted caster-centered CoA Range 50. */
|
||||
export function romDruidMageInspirationRecipientIds(
|
||||
sourcePosition: RomDruidMageInspirationPosition,
|
||||
candidates: readonly RomDruidMageInspirationRecipient[],
|
||||
): readonly CombatEntityId[] {
|
||||
const radiusSquared = ROM_DRUID_MAGE_INSPIRATION_RADIUS ** 2;
|
||||
return [...new Set(candidates.filter((candidate) => {
|
||||
if (!candidate.living || !candidate.position) return false;
|
||||
const dx = candidate.position[0] - sourcePosition[0];
|
||||
const dy = candidate.position[1] - sourcePosition[1];
|
||||
const dz = candidate.position[2] - sourcePosition[2];
|
||||
return dx * dx + dy * dy + dz * dz <= radiusSquared;
|
||||
}).map((candidate) => candidate.id))];
|
||||
}
|
||||
|
||||
export function romDruidMageInspirationAuraDefinition(): AuraDefinition {
|
||||
return {
|
||||
id: "rom:druid-mage:inspiration",
|
||||
name: "Inspiration",
|
||||
description: "Increases physical attack speed and casting speed by 2% per stack.",
|
||||
disposition: "buff",
|
||||
durationMs: ROM_DRUID_MAGE_INSPIRATION_DURATION_MS,
|
||||
maxStacks: ROM_DRUID_MAGE_INSPIRATION_MAX_STACKS,
|
||||
stackBehavior: "add",
|
||||
uniqueBySource: false,
|
||||
utilityTags: [ROM_DRUID_MAGE_INSPIRATION_AURA_TAG],
|
||||
modifiers: [
|
||||
{
|
||||
kind: "stat",
|
||||
stat: "melee-haste",
|
||||
operation: "flat",
|
||||
value: ROM_DRUID_MAGE_INSPIRATION_BONUS_PER_STACK_PERCENT,
|
||||
perStack: true,
|
||||
},
|
||||
{
|
||||
kind: "stat",
|
||||
stat: "ranged-haste",
|
||||
operation: "flat",
|
||||
value: ROM_DRUID_MAGE_INSPIRATION_BONUS_PER_STACK_PERCENT,
|
||||
perStack: true,
|
||||
},
|
||||
{
|
||||
kind: "stat",
|
||||
stat: "spell-haste",
|
||||
operation: "flat",
|
||||
value: ROM_DRUID_MAGE_INSPIRATION_BONUS_PER_STACK_PERCENT,
|
||||
perStack: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/** Apply one stack to each recipient selected by the caller; the shared aura refreshes for 10 seconds. */
|
||||
export function romDruidMageInspirationApplySpendStack(
|
||||
current: readonly ActiveAura[],
|
||||
sourceId: CombatEntityId,
|
||||
recipientIds: readonly CombatEntityId[],
|
||||
now: number,
|
||||
): readonly ActiveAura[] {
|
||||
let auras = current;
|
||||
const definition = romDruidMageInspirationAuraDefinition();
|
||||
for (const targetId of new Set(recipientIds)) {
|
||||
auras = applyAura(auras, { definition, sourceId, targetId, now, stacks: 1 }).auras;
|
||||
}
|
||||
return auras;
|
||||
}
|
||||
|
||||
export function romDruidMageInspirationHastePercent(
|
||||
auras: readonly ActiveAura[],
|
||||
targetId: string,
|
||||
now: number,
|
||||
): number {
|
||||
return auras
|
||||
.filter((aura) => aura.targetId === targetId
|
||||
&& aura.definition.utilityTags?.includes(ROM_DRUID_MAGE_INSPIRATION_AURA_TAG)
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now))
|
||||
.reduce((total, aura) => total + romDruidMageInspirationBonusPercent(aura.stacks), 0);
|
||||
}
|
||||
|
||||
export function isRomDruidMagePair(row: {
|
||||
readonly classId?: string;
|
||||
readonly secondaryClassId?: string | null;
|
||||
}): boolean {
|
||||
return row.classId === "rom-druid" && row.secondaryClassId === "rom-mage";
|
||||
}
|
||||
|
||||
/**
|
||||
* CoA gives one Inspiration stack when the caster spends Nature's Power.
|
||||
* This resolves one positive resource-spend event, not each point spent.
|
||||
*/
|
||||
export function romDruidMageInspirationStacksAfterSpend(
|
||||
currentStacks: number,
|
||||
spentNaturePower: number,
|
||||
passiveLearned: boolean,
|
||||
): number {
|
||||
const stacks = Math.max(0, Math.min(ROM_DRUID_MAGE_INSPIRATION_MAX_STACKS, Math.floor(currentStacks)));
|
||||
if (!passiveLearned || !(spentNaturePower > 0)) return stacks;
|
||||
return Math.min(ROM_DRUID_MAGE_INSPIRATION_MAX_STACKS, stacks + 1);
|
||||
}
|
||||
|
||||
export function romDruidMageInspirationBonusPercent(stacks: number): number {
|
||||
const boundedStacks = Math.max(0, Math.min(ROM_DRUID_MAGE_INSPIRATION_MAX_STACKS, Math.floor(stacks)));
|
||||
return boundedStacks * ROM_DRUID_MAGE_INSPIRATION_BONUS_PER_STACK_PERCENT;
|
||||
}
|
||||
Reference in new issue
Block a user