diablo2-web/tests/client/derived-stats.test.ts

57 lines
2.3 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
RESIST_PENALTY_BY_DIFFICULTY,
computeCharacterSheetDerived,
} from '../../src/client/ui-model/derived-stats.ts'
function statsOf(entries: Record<number, number>) {
return { get: (id: number) => entries[id] ?? 0 }
}
describe('character sheet derived values (1.13c tables)', () => {
it('reads ResistPenalty from DifficultyLevels.txt', () => {
expect(RESIST_PENALTY_BY_DIFFICULTY).toEqual([0, -40, -100])
})
it('level-1 Assassin (dex 20, no bonuses)', () => {
const d = computeCharacterSheetDerived('ass', statsOf({ 2: 20 }), 0)
expect(d.attackRating).toBe((20 - 7) * 5 + 15)
expect(d.defense).toBe(5)
expect([d.fireRes, d.coldRes, d.lightRes, d.poisRes]).toEqual([0, 0, 0, 0])
})
it('applies difficulty penalty and the 75 + max-resist cap', () => {
const d = computeCharacterSheetDerived('sor', statsOf({ 2: 25, 39: 200, 40: 5, 43: 30 }), 2)
expect(d.fireRes).toBe(80)
expect(d.coldRes).toBe(-70)
expect(d.attackRating).toBe((25 - 7) * 5 - 15)
})
it('clamps maximum resistance at the hard cap of 95% even if max-resist stat is +25 or higher', () => {
const d = computeCharacterSheetDerived('pal', statsOf({ 2: 20, 39: 100, 40: 25, 41: 100, 42: 30 }), 0)
expect(d.fireRes).toBe(95)
expect(d.lightRes).toBe(95)
})
it('calculates defense per UNITS_GetDefense with item/skill armor percent and override percent', () => {
// dex 20 (base def = 5) + armorclass 100 = baseDef 105
// item_armor_percent (16) = 50, skill_armor_percent (171) = 50 -> armorPercent = 100%
// defenseBonus = floor(105 * 100 / 100) = 105 -> totalDefense = 210
const d = computeCharacterSheetDerived('bar', statsOf({ 2: 20, 31: 100, 16: 50, 171: 50 }), 0)
expect(d.defense).toBe(210)
// Berserk: armor_override_percent (182) = -100 -> defense drops to 0
const dBerserk = computeCharacterSheetDerived('bar', statsOf({ 2: 20, 31: 100, 16: 50, 171: 50, 182: -100 }), 0)
expect(dBerserk.defense).toBe(0)
})
it('drops defense to 0 when running is true', () => {
const dRunning = computeCharacterSheetDerived('bar', statsOf({ 2: 20, 31: 100 }), 0, true)
expect(dRunning.defense).toBe(0)
const dWalking = computeCharacterSheetDerived('bar', statsOf({ 2: 20, 31: 100 }), 0, false)
expect(dWalking.defense).toBe(105)
})
})