forked from phenom/healer-man
Validate RoM primary five hybrid stats and pair mechanics
This commit is contained in:
1 parent
8e95480d4f
commit
c8b3a3d95a
18 files changed
+2061
-48
No files matched your search
@@ -14,6 +14,9 @@ import {
|
||||
type RomGeneratedAbilityLike,
|
||||
type RomSourceComponent,
|
||||
} from "./romAbilitySemantics";
|
||||
import { overrideRomPriestKnightEffects } from "./romPriestKnightOverrides";
|
||||
import { overrideRomScoutRogueEffects } from "./romScoutRogueOverrides";
|
||||
import { overrideRomMageWarlockEffects } from "./romMageWarlockOverrides";
|
||||
|
||||
export type RomSkillGroup = "primary" | "general" | "elite" | "passive";
|
||||
|
||||
@@ -204,7 +207,7 @@ function auraModifiers(semantic: Extract<RomAbilitySemantic, { kind: "aura" }>):
|
||||
}
|
||||
if (type === 142) return magicDamageTaken(-percent(value));
|
||||
if (type === 143) return [{ kind: "damage", direction: "taken", school: "physical", operation: "percent", value: -percent(value) }];
|
||||
if (type === 144) return [{ kind: "healing", direction: "taken", operation: "percent", value: -percent(value) }];
|
||||
if (type === 144) return [{ kind: "healing", direction: "taken", operation: "percent", value: percent(value) }];
|
||||
if (type === 148) return [{ kind: "damage", direction: "dealt", attackKind: "spell", operation: "flat", value }];
|
||||
if (type === 149) return [{ kind: "healing", direction: "done", operation: "percent", value: percent(value) }];
|
||||
if (type === 150) return [{ kind: "healing", direction: "done", operation: "flat", value }];
|
||||
@@ -271,8 +274,7 @@ function semanticEffect(row: GeneratedSemanticRow, semantic: RomAbilitySemantic,
|
||||
const sourcePoints = coefficientCalculation ? 0 : rawPower;
|
||||
const pointsPerLevel = sourcePoints * Math.max(0, semantic.powerPerSkillLevel) / 100;
|
||||
const basePoints = sourcePoints + pointsPerLevel;
|
||||
return {
|
||||
kind: semantic.kind,
|
||||
const amount = {
|
||||
coefficient,
|
||||
...(coefficientPerLevel > 0 ? { coefficientPerLevel, sourceLevel: 1 } : {}),
|
||||
...(basePoints > 0 ? {
|
||||
@@ -281,9 +283,16 @@ function semanticEffect(row: GeneratedSemanticRow, semantic: RomAbilitySemantic,
|
||||
sourceLevel: 1,
|
||||
...(coefficient > 0 ? { bonusPowerCoefficient: coefficient } : {}),
|
||||
} : {}),
|
||||
...(semantic.kind === "heal" && semantic.percentMaxHealth !== undefined
|
||||
? { percentMaxHealth: semantic.percentMaxHealth }
|
||||
: {}),
|
||||
};
|
||||
if (semantic.kind === "damage") return {
|
||||
kind: "damage", ...amount,
|
||||
...(combatSchool(semantic.school) ? { school: combatSchool(semantic.school) } : {}),
|
||||
};
|
||||
return {
|
||||
kind: "heal", ...amount,
|
||||
...(semantic.percentMaxHealth !== undefined ? { percentMaxHealth: semantic.percentMaxHealth } : {}),
|
||||
...(/\b(?:all teammates|(?:all|every) party members?)\b/i.test(row.description)
|
||||
? { scope: "party" as const } : {}),
|
||||
};
|
||||
}
|
||||
if (semantic.kind === "dot" || semantic.kind === "hot") {
|
||||
@@ -292,6 +301,9 @@ function semanticEffect(row: GeneratedSemanticRow, semantic: RomAbilitySemantic,
|
||||
return {
|
||||
kind: semantic.kind,
|
||||
coefficient: 0,
|
||||
...(semantic.kind === "dot" && combatSchool(semantic.school)
|
||||
? { school: combatSchool(semantic.school) }
|
||||
: {}),
|
||||
basePoints: Math.max(0, semantic.basePerTick) * (1 + Math.max(0, semantic.perSkillLevel) / 100),
|
||||
pointsPerLevel: Math.max(0, semantic.basePerTick) * Math.max(0, semantic.perSkillLevel) / 100,
|
||||
sourceLevel: 1,
|
||||
@@ -327,7 +339,7 @@ function semanticEffect(row: GeneratedSemanticRow, semantic: RomAbilitySemantic,
|
||||
if (semantic.kind === "aura") {
|
||||
const recipient = auraRecipient(row, semantic);
|
||||
const modifiers = auraModifiers(semantic);
|
||||
const partyScope = /\b(?:all )?party members?\b/i.test(row.description);
|
||||
const partyScope = /\b(?:(?:all|every) party members?|all teammates)\b/i.test(row.description);
|
||||
const aura: AuraDefinition = {
|
||||
id: `rom-aura-${effectId}-${slug(semantic.effect)}`,
|
||||
name: `${row.name}: ${semantic.effect}`,
|
||||
@@ -450,6 +462,21 @@ function stateAuraEffect(
|
||||
}
|
||||
|
||||
function specializedEffects(row: GeneratedSemanticRow): readonly AbilityEffect[] | null {
|
||||
if (row.sourceSkillId === 498744) {
|
||||
return [{ kind: "cooldown-reset", mode: "reset", abilityName: "Electrostatic Charge" }];
|
||||
}
|
||||
if (row.sourceSkillId === 498758) {
|
||||
return [
|
||||
{ kind: "cooldown-reset", mode: "reset", abilityName: "Intensification" },
|
||||
{ kind: "cooldown-reset", mode: "reset", abilityName: "Energy Well" },
|
||||
];
|
||||
}
|
||||
if (row.sourceSkillId === 498759) {
|
||||
// Its own cooldown resets only after the two hits leave the target alive.
|
||||
return translateRomAbilitySemantics(row)
|
||||
.map((semantic, index) => semanticEffect(row, semantic, index))
|
||||
.filter((effect) => effect.kind === "damage");
|
||||
}
|
||||
if (row.sourceSkillId === 491163) {
|
||||
return [stateAuraEffect(row, "frost-arrow", "Frost Arrow", 600_000, {
|
||||
procs: [{
|
||||
@@ -497,6 +524,10 @@ function effects(row: GeneratedSemanticRow): readonly AbilityEffect[] {
|
||||
const specialized = specializedEffects(row);
|
||||
if (specialized) return specialized;
|
||||
const translated = translateRomAbilitySemantics(row).map((semantic, index) => semanticEffect(row, semantic, index));
|
||||
const pairOverride = overrideRomPriestKnightEffects(row, translated)
|
||||
?? overrideRomScoutRogueEffects(row, translated)
|
||||
?? overrideRomMageWarlockEffects(row, translated);
|
||||
if (pairOverride) return pairOverride;
|
||||
const outputStrength = (effect: AbilityEffect): number => (
|
||||
"coefficient" in effect ? effect.coefficient : 0
|
||||
) + ("coefficientPerLevel" in effect ? effect.coefficientPerLevel ?? 0 : 0)
|
||||
@@ -640,9 +671,18 @@ function effects(row: GeneratedSemanticRow): readonly AbilityEffect[] {
|
||||
}
|
||||
|
||||
function areaRadius(row: Pick<GeneratedSkill, "description" | "range">): number | undefined {
|
||||
if (!/\b(?:multiple targets|all targets|surrounding (?:enemies|targets)|party members within|friendly targets within|targets within (?:a )?range)\b/i.test(row.description)) return undefined;
|
||||
if (row.description.includes("up to 3 targets") && row.description.includes("Inflicts")) return 6;
|
||||
if (!/\b(?:multiple targets|all targets|surrounding (?:enemies|targets)|party members within|all teammates within|friendly targets within|(?:enemies|targets) within (?:a )?range)\b/i.test(row.description)) return undefined;
|
||||
const sourceRange = row.description.match(/\b(?:range|radius) of\s+(\d+(?:\.\d+)?)/i)?.[1];
|
||||
return Math.max(3, Math.min(40, sourceRange ? Number(sourceRange) / 10 : row.range || 8));
|
||||
return Math.max(3, Math.min(40, sourceRange ? Number(sourceRange) / 10 : row.range > 1 ? row.range : 10));
|
||||
}
|
||||
|
||||
function sourceTargeting(sourceSkillId: number): AbilityDefinition["targeting"] | undefined {
|
||||
if (sourceSkillId === 490400) return { shape: "line", maximumTargets: 8 };
|
||||
if (sourceSkillId === 490457) return { shape: "chain", maximumTargets: 3, jumpRange: 6 };
|
||||
if (sourceSkillId === 492920) return { shape: "circle", radius: 5, maximumTargets: 8 };
|
||||
if (sourceSkillId === 497959 || sourceSkillId === 497966) return { shape: "circle", radius: 6, maximumTargets: 3 };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function rank(row: GeneratedRank, parent: GeneratedSkill): AbilityRankDefinition {
|
||||
@@ -685,7 +725,10 @@ function ability(row: GeneratedSkill): AbilityDefinition {
|
||||
description: row.description,
|
||||
target: row.target,
|
||||
range: { min: 0, max: row.range },
|
||||
...(areaRadius(row) !== undefined ? { radius: areaRadius(row) } : {}),
|
||||
...(sourceTargeting(row.sourceSkillId)?.radius !== undefined
|
||||
? { radius: sourceTargeting(row.sourceSkillId)!.radius }
|
||||
: areaRadius(row) !== undefined ? { radius: areaRadius(row) } : {}),
|
||||
...(sourceTargeting(row.sourceSkillId) ? { targeting: sourceTargeting(row.sourceSkillId) } : {}),
|
||||
castMode: row.castTimeMs > 0 ? "cast" : "instant",
|
||||
castTimeMs: row.castTimeMs,
|
||||
cooldownMs: row.cooldownMs,
|
||||
|
||||
Reference in new issue
Block a user