import { describe, expect, it } from "vitest"; import { EMPTY_ITEM_STATS } from "./itemStats"; import { baseAttributesFor, baseResourcesFor, damageAfterEquipmentMitigation, deriveCharacterStats, hastedDuration, healthFromTotalStamina, MANA_POOL_MULTIPLIER, ratingMultiplier, ratingPerPercent, } from "./wotlkStats"; describe("WoW 3.3.5a character stats", () => { it("uses the authored race/class base-stat table", () => { expect(baseAttributesFor("warrior", 1, "human")).toEqual({ strength: 23, agility: 20, stamina: 22, intellect: 20, spirit: 20, }); }); it("uses exact client combat-rating conversions at low level and level 80", () => { expect(ratingMultiplier("meleeHit", 20)).toBeCloseTo(0.433333, 5); expect(ratingPerPercent("meleeHit", 80)).toBeCloseTo(32.789989, 5); expect(ratingPerPercent("spellHit", 80)).toBeCloseTo(26.231993, 5); expect(ratingPerPercent("meleeCrit", 80)).toBeCloseTo(45.905987, 5); expect(ratingMultiplier("meleeHaste", 20, "paladin")).toBeGreaterThan( ratingMultiplier("meleeHaste", 20, "warrior"), ); }); it("turns primary attributes into class resources and combat power", () => { const warriorBase = deriveCharacterStats("warrior", 20, EMPTY_ITEM_STATS); const warriorGeared = deriveCharacterStats("warrior", 20, { ...EMPTY_ITEM_STATS, strength: 10, stamina: 10, }); expect(warriorGeared.attackPower - warriorBase.attackPower).toBe(20); expect(warriorGeared.health - warriorBase.health).toBe(100); const mageBase = deriveCharacterStats("mage", 20, EMPTY_ITEM_STATS); const mageGeared = deriveCharacterStats("mage", 20, { ...EMPTY_ITEM_STATS, intellect: 10, spirit: 10, spellPower: 7, mp5: 4, }); expect(mageGeared.mana - mageBase.mana).toBe(150 * MANA_POOL_MULTIPLIER); expect(mageGeared.spellPower).toBe(7); expect(mageGeared.spellCritPercent).toBeGreaterThan(mageBase.spellCritPercent); expect(mageGeared.manaPer5).toBeGreaterThan(mageBase.manaPer5 + 4); }); it("applies automatic Ascension racial passives to starting attributes and pools", () => { const humanMage = deriveCharacterStats("mage", 1, EMPTY_ITEM_STATS, "human"); const gnomeMage = deriveCharacterStats("mage", 1, EMPTY_ITEM_STATS, "gnome"); expect(gnomeMage.intellect).toBeGreaterThan(baseAttributesFor("mage", 1, "gnome").intellect); expect(gnomeMage.mana).toBeGreaterThan(humanMage.mana); const taurenAttributes = baseAttributesFor("warrior", 1, "tauren"); const taurenWithoutEndurance = baseResourcesFor("warrior", 1).health + healthFromTotalStamina(taurenAttributes.stamina); const taurenWarrior = deriveCharacterStats("warrior", 1, EMPTY_ITEM_STATS, "tauren"); expect(taurenWarrior.health).toBeGreaterThan(taurenWithoutEndurance); }); it("makes haste, armor, avoidance, block, and resistances affect combat", () => { expect(hastedDuration(2_000, 25)).toBe(1_600); const physical = damageAfterEquipmentMitigation(100, { ...EMPTY_ITEM_STATS, strength: 8, agility: 8, armor: 200, defenseRating: 6, dodgeRating: 5, blockRating: 5, blockValue: 10, }, 20); const fire = damageAfterEquipmentMitigation(100, { ...EMPTY_ITEM_STATS, fireResistance: 100, }, 20, "fire", 20); expect(physical).toBeLessThan(100); expect(fire).toBeLessThan(100); }); });