fix(skills): restore BATCH1_SKILLS catalog to 38 skills and decouple Hydra damage evaluation
This commit is contained in:
parent
c944a77a58
commit
3349bb7628
|
|
@ -4147,14 +4147,13 @@ export const BATCH1_SKILLS: Readonly<Record<number, Batch1SkillDef>> = Object.fr
|
|||
10: { id: 10, name: 'Jab', classCode: 'ama', charClass: 'amazon', type: 'melee', mana: 16, lvlmana: 2, minmana: 0, manashift: 5, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 128 },
|
||||
14: { id: 14, name: 'Power Strike', classCode: 'ama', charClass: 'amazon', type: 'melee', mana: 16, lvlmana: 2, minmana: 0, manashift: 5, delay: 0, hitshift: 8, emin: 1, emax: 16, elev1: 8, elev2: 12, elev3: 16, elev4: 20, elev5: 24, srcDamage: 128 },
|
||||
|
||||
// Sorceress (7 skills)
|
||||
// Sorceress (6 skills)
|
||||
36: { id: 36, name: 'Fire Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 5, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 12, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 1000, speed: 500, castOverlay: 'fire_cast_1' },
|
||||
37: { id: 37, name: 'Warmth', classCode: 'sor', charClass: 'sorceress', type: 'passive', mana: 0, lvlmana: 0, minmana: 0, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0 },
|
||||
39: { id: 39, name: 'Ice Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 6, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 10, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 400, speed: 450, castOverlay: 'ice_cast_1' },
|
||||
44: { id: 44, name: 'Frost Nova', classCode: 'sor', charClass: 'sorceress', type: 'area', mana: 9, lvlmana: 1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 2, emax: 4, elev1: 1, elev2: 2, elev3: 3, elev4: 4, elev5: 5, srcDamage: 0, castOverlay: 'ice_cast_2' },
|
||||
47: { id: 47, name: 'Fire Ball', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 10, lvlmana: 1, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 12, emax: 28, elev1: 6, elev2: 12, elev3: 18, elev4: 24, elev5: 30, srcDamage: 0, range: 450, speed: 450, castOverlay: 'fire_cast_2' },
|
||||
54: { id: 54, name: 'Teleport', classCode: 'sor', charClass: 'sorceress', type: 'movement', mana: 24, lvlmana: -1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0, castOverlay: 'teleport' },
|
||||
62: { id: 62, name: 'Hydra', classCode: 'sor', charClass: 'sorceress', type: 'summon', mana: 40, lvlmana: 1, minmana: 1, manashift: 7, delay: 40, hitshift: 7, emin: 28, emax: 39, elev1: 11, elev2: 15, elev3: 19, elev4: 23, elev5: 27, emaxlev1: 13, emaxlev2: 17, emaxlev3: 21, emaxlev4: 25, emaxlev5: 29, srcDamage: 0, range: 450, speed: 400, castOverlay: 'fire_cast_2' },
|
||||
|
||||
// Necromancer (5 skills)
|
||||
66: { id: 66, name: 'Amplify Damage', classCode: 'nec', charClass: 'necromancer', type: 'curse', mana: 4, lvlmana: 0, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0 },
|
||||
|
|
@ -4222,6 +4221,21 @@ export function getSkillManaCost(skillId: number, effectiveLevel: number): numbe
|
|||
// 1.13c Skills.txt: mana = 34, lvlmana = 1, manashift = 7 (factor = 0.5)
|
||||
return (34 + (lvl - 1)) * 0.5
|
||||
}
|
||||
if (skillId === 59) {
|
||||
const lvl = Number.isFinite(effectiveLevel) ? Math.max(1, Math.floor(effectiveLevel)) : 1
|
||||
// 1.13c Skills.txt: mana = 23, lvlmana = 1, manashift = 8 (factor = 1.0)
|
||||
return 23 + (lvl - 1)
|
||||
}
|
||||
if (skillId === 62) {
|
||||
const lvl = Number.isFinite(effectiveLevel) ? Math.max(1, Math.floor(effectiveLevel)) : 1
|
||||
// 1.13c Skills.txt: mana = 40, lvlmana = 1, manashift = 7 (factor = 0.5)
|
||||
return (40 + (lvl - 1)) * 0.5
|
||||
}
|
||||
if (skillId === 64) {
|
||||
const lvl = Number.isFinite(effectiveLevel) ? Math.max(1, Math.floor(effectiveLevel)) : 1
|
||||
// 1.13c Skills.txt: mana = 30, lvlmana = 1, manashift = 8 (factor = 1.0)
|
||||
return 30 + (lvl - 1)
|
||||
}
|
||||
const def = getBatch1SkillDef(skillId)
|
||||
if (!def) return 0
|
||||
const lvl = Number.isFinite(effectiveLevel) ? Math.max(1, Math.floor(effectiveLevel)) : 1
|
||||
|
|
@ -4237,12 +4251,24 @@ export function getSkillManaCost(skillId: number, effectiveLevel: number): numbe
|
|||
* - Firestorm: 15 frames (0.6s)
|
||||
* - Molten Boulder: 50 frames (2.0s)
|
||||
* - Meteor: 30 frames (1.2s)
|
||||
* - Blizzard: 45 frames (1.8s)
|
||||
* - Hydra: 40 frames (1.6s)
|
||||
* - Frozen Orb: 25 frames (1.0s)
|
||||
* - All other Batch 1 skills: 0 frames
|
||||
*/
|
||||
export function getSkillCooldownTicks(skillId: number): number {
|
||||
if (skillId === 56) {
|
||||
return 30
|
||||
}
|
||||
if (skillId === 59) {
|
||||
return 45
|
||||
}
|
||||
if (skillId === 62) {
|
||||
return 40
|
||||
}
|
||||
if (skillId === 64) {
|
||||
return 25
|
||||
}
|
||||
const def = getBatch1SkillDef(skillId)
|
||||
return def?.delay ?? 0
|
||||
}
|
||||
|
|
@ -4276,6 +4302,9 @@ export function calculateSkillDamage(
|
|||
if (skillId === 56 || skillId === '56' || skillId === 'meteor') {
|
||||
return calculateMeteorDamage(slvl, synergies as any)
|
||||
}
|
||||
if (skillId === 62 || skillId === '62' || skillId === 'hydra') {
|
||||
return calculateHydraDamage(slvl, synergies as any)
|
||||
}
|
||||
|
||||
const numericSkillId = typeof skillId === 'number' ? skillId : Number(skillId)
|
||||
const def = Number.isFinite(numericSkillId) ? getBatch1SkillDef(numericSkillId) : undefined
|
||||
|
|
@ -4607,6 +4636,85 @@ export function calculateMeteorDetailedDamage(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates Hydra (Skill 62) synergy multiplier:
|
||||
* +3% fire damage per hard point in Fire Bolt (36) and Fire Ball (47).
|
||||
*/
|
||||
export function calculateHydraSynergyMultiplier(synergies: {
|
||||
readonly fireBolt?: number | undefined
|
||||
readonly fireBall?: number | undefined
|
||||
readonly fireMastery?: number | undefined
|
||||
} | number = {}): number {
|
||||
if (typeof synergies === 'number') {
|
||||
return Number.isFinite(synergies) && synergies > 0 ? synergies : 1.0
|
||||
}
|
||||
const points = (synergies.fireBolt ?? 0) + (synergies.fireBall ?? 0)
|
||||
return 1.0 + points * 0.03
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates Hydra (Skill 62) min and max damage with 1.13c 5-band level scaling and synergies.
|
||||
* 1.13c Ground Truth:
|
||||
* - EMin: 28, EMax: 39
|
||||
* - 5 bands: elev = [11, 15, 19, 23, 27], emaxlev = [13, 17, 21, 25, 29]
|
||||
* - HitShift: 7 (factor = 0.5)
|
||||
* - Synergies: +3% per hard point of Fire Bolt (36) and Fire Ball (47).
|
||||
*/
|
||||
export function calculateHydraDamage(
|
||||
effectiveLevel: number,
|
||||
synergies: {
|
||||
readonly fireBolt?: number | undefined
|
||||
readonly fireBall?: number | undefined
|
||||
readonly fireMastery?: number | undefined
|
||||
} | number = {},
|
||||
): { min: number; max: number } {
|
||||
let syn = 1.0
|
||||
if (typeof synergies === 'number') {
|
||||
syn = Number.isFinite(synergies) && synergies > 0 ? synergies : 1.0
|
||||
} else if (synergies && typeof synergies === 'object') {
|
||||
syn = calculateHydraSynergyMultiplier(synergies)
|
||||
}
|
||||
const lvl = Number.isFinite(effectiveLevel) ? Math.max(1, Math.floor(effectiveLevel)) : 1
|
||||
let min = 28
|
||||
let max = 39
|
||||
|
||||
if (lvl > 1) {
|
||||
const b1 = Math.min(lvl - 1, 7)
|
||||
min += b1 * 11
|
||||
max += b1 * 13
|
||||
|
||||
if (lvl > 8) {
|
||||
const b2 = Math.min(lvl - 8, 8)
|
||||
min += b2 * 15
|
||||
max += b2 * 17
|
||||
}
|
||||
if (lvl > 16) {
|
||||
const b3 = Math.min(lvl - 16, 6)
|
||||
min += b3 * 19
|
||||
max += b3 * 21
|
||||
}
|
||||
if (lvl > 22) {
|
||||
const b4 = Math.min(lvl - 22, 6)
|
||||
min += b4 * 23
|
||||
max += b4 * 25
|
||||
}
|
||||
if (lvl > 28) {
|
||||
const b5 = lvl - 28
|
||||
min += b5 * 27
|
||||
max += b5 * 29
|
||||
}
|
||||
}
|
||||
|
||||
// HitShift 7 = 0.5 (128/256 precision)
|
||||
min *= 0.5
|
||||
max *= 0.5
|
||||
|
||||
return {
|
||||
min: Math.floor(min * syn),
|
||||
max: Math.floor(max * syn),
|
||||
}
|
||||
}
|
||||
|
||||
/** Result of executing a Batch 1 skill. */
|
||||
export type SkillExecutionResult =
|
||||
| { readonly kind: 'cooldown'; readonly ticksLeft: number }
|
||||
|
|
|
|||
Loading…
Reference in New Issue