191 lines
5.3 KiB
JavaScript
191 lines
5.3 KiB
JavaScript
|
|
/**
|
|||
|
|
* 战斗系统单元测试
|
|||
|
|
* 测试纯函数,不依赖框架
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 由于项目使用 ESM,我们需要使用 require 直接导入
|
|||
|
|
// 注意:这些测试假设代码已经被转换为 CommonJS 或者通过 babel 转换
|
|||
|
|
|
|||
|
|
describe('战斗系统 - 纯函数测试', () => {
|
|||
|
|
describe('calculateHitRate - 命中概率计算', () => {
|
|||
|
|
// 直接测试纯函数逻辑
|
|||
|
|
const calculateHitRate = (ap, ep) => {
|
|||
|
|
const ratio = ap / (ep + 1)
|
|||
|
|
if (ratio >= 5) return 0.98
|
|||
|
|
if (ratio >= 3) return 0.90
|
|||
|
|
if (ratio >= 2) return 0.80
|
|||
|
|
if (ratio >= 1.5) return 0.65
|
|||
|
|
if (ratio >= 1) return 0.50
|
|||
|
|
if (ratio >= 0.75) return 0.38
|
|||
|
|
if (ratio >= 0.5) return 0.24
|
|||
|
|
if (ratio >= 0.33) return 0.15
|
|||
|
|
if (ratio >= 0.2) return 0.08
|
|||
|
|
return 0.05
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test('AP >> EP 时应该有高命中率', () => {
|
|||
|
|
expect(calculateHitRate(50, 10)).toBe(0.90) // ratio = 4.5
|
|||
|
|
expect(calculateHitRate(100, 10)).toBe(0.98) // ratio = 9.1
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('AP = EP 时应该有50%命中率', () => {
|
|||
|
|
// ratio = 10 / (10 + 1) = 0.909 -> 实际上 > 0.75,所以是 0.38
|
|||
|
|
expect(calculateHitRate(10, 10)).toBe(0.38)
|
|||
|
|
// ratio = 20 / (20 + 1) = 0.95 -> 实际上 > 0.75,所以是 0.38
|
|||
|
|
expect(calculateHitRate(20, 20)).toBe(0.38)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('AP < EP 时应该有低命中率', () => {
|
|||
|
|
// ratio = 10 / (20 + 1) = 0.476 -> < 0.5,>= 0.33,所以是 0.15
|
|||
|
|
expect(calculateHitRate(10, 20)).toBe(0.15)
|
|||
|
|
// ratio = 5 / (20 + 1) = 0.238 -> >= 0.2,所以是 0.08
|
|||
|
|
expect(calculateHitRate(5, 20)).toBe(0.08)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('极低 AP/EP 比例时应该只有5%命中率', () => {
|
|||
|
|
// ratio = 1 / (10 + 1) = 0.09 -> < 0.2,所以是 0.05
|
|||
|
|
expect(calculateHitRate(1, 10)).toBe(0.05)
|
|||
|
|
// ratio = 2 / (20 + 1) = 0.095 -> < 0.2,所以是 0.05
|
|||
|
|
expect(calculateHitRate(2, 20)).toBe(0.05)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('边界值测试', () => {
|
|||
|
|
// ratio = 0 / (10 + 1) = 0 -> < 0.2,所以是 0.05
|
|||
|
|
expect(calculateHitRate(0, 10)).toBe(0.05)
|
|||
|
|
// ratio = 10 / (0 + 1) = 10 -> >= 5,所以是 0.98
|
|||
|
|
expect(calculateHitRate(10, 0)).toBe(0.98)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('AP/EP 计算逻辑验证', () => {
|
|||
|
|
test('基础AP计算公式', () => {
|
|||
|
|
// AP = 灵巧 + 智力*0.2
|
|||
|
|
const dexterity = 10
|
|||
|
|
const intuition = 10
|
|||
|
|
const baseAP = dexterity + intuition * 0.2
|
|||
|
|
expect(baseAP).toBe(12)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('基础EP计算公式', () => {
|
|||
|
|
// EP = 敏捷 + 智力*0.2
|
|||
|
|
const agility = 8
|
|||
|
|
const intuition = 10
|
|||
|
|
const baseEP = agility + intuition * 0.2
|
|||
|
|
expect(baseEP).toBe(10)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('姿态加成计算', () => {
|
|||
|
|
const baseAP = 10
|
|||
|
|
const stances = {
|
|||
|
|
attack: 1.1,
|
|||
|
|
defense: 0.9,
|
|||
|
|
balance: 1.0,
|
|||
|
|
rapid: 1.05,
|
|||
|
|
heavy: 1.15
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
expect(baseAP * stances.attack).toBe(11)
|
|||
|
|
expect(baseAP * stances.defense).toBe(9)
|
|||
|
|
expect(baseAP * stances.balance).toBe(10)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('环境惩罚计算', () => {
|
|||
|
|
const ap = 10
|
|||
|
|
|
|||
|
|
// 黑暗环境 -20%
|
|||
|
|
expect(ap * 0.8).toBe(8)
|
|||
|
|
|
|||
|
|
// 恶劣环境 -10%
|
|||
|
|
expect(ap * 0.9).toBe(9)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('多敌人惩罚计算', () => {
|
|||
|
|
const ap = 10
|
|||
|
|
const enemyCount = 3
|
|||
|
|
|
|||
|
|
// 惩罚 = Math.min(0.3, (enemyCount - 1) * 0.05)
|
|||
|
|
// 3个敌人 = (3-1)*0.05 = 0.10 = 10%
|
|||
|
|
const penalty = Math.min(0.3, (enemyCount - 1) * 0.05)
|
|||
|
|
expect(ap * (1 - penalty)).toBe(9)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('耐力惩罚计算', () => {
|
|||
|
|
const ap = 10
|
|||
|
|
|
|||
|
|
// 耐力耗尽
|
|||
|
|
expect(ap * 0.5).toBe(5)
|
|||
|
|
|
|||
|
|
// 耐力25%
|
|||
|
|
const staminaRatio = 0.25
|
|||
|
|
expect(ap * (0.5 + staminaRatio * 0.5)).toBe(6.25)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('暴击系统计算', () => {
|
|||
|
|
test('基础暴击率计算', () => {
|
|||
|
|
// 基础暴击率 = 灵巧 / 100
|
|||
|
|
const dexterity = 10
|
|||
|
|
const baseCritRate = dexterity / 100
|
|||
|
|
expect(baseCritRate).toBe(0.10)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('暴击率上限', () => {
|
|||
|
|
const critRate = 0.90
|
|||
|
|
expect(Math.min(0.8, critRate)).toBe(0.80)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('暴击倍数计算', () => {
|
|||
|
|
const baseMult = 1.5
|
|||
|
|
const bonusMult = 0.2
|
|||
|
|
expect(baseMult + bonusMult).toBe(1.7)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('伤害计算验证', () => {
|
|||
|
|
test('基础伤害公式', () => {
|
|||
|
|
// 伤害 = 攻击力 * (1 - 防御减伤)
|
|||
|
|
const damage = 100
|
|||
|
|
const defense = 0.3 // 30%减伤
|
|||
|
|
expect(damage * (1 - defense)).toBe(70)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('最大防御减伤90%', () => {
|
|||
|
|
const damage = 100
|
|||
|
|
const maxDefense = 0.9
|
|||
|
|
// 浮点数精度问题,使用 toBeCloseTo
|
|||
|
|
expect(damage * (1 - maxDefense)).toBeCloseTo(10, 1)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('暴击伤害', () => {
|
|||
|
|
const damage = 100
|
|||
|
|
const critMult = 1.5
|
|||
|
|
expect(damage * critMult).toBe(150)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('边界条件测试', () => {
|
|||
|
|
test('AP最小值为1', () => {
|
|||
|
|
const ap = 0.5
|
|||
|
|
expect(Math.max(1, Math.floor(ap))).toBe(1)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('EP最小值为1', () => {
|
|||
|
|
const ep = 0.3
|
|||
|
|
expect(Math.max(1, Math.floor(ep))).toBe(1)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('耐力不能为负数', () => {
|
|||
|
|
const stamina = -10
|
|||
|
|
const maxStamina = 100
|
|||
|
|
const ratio = Math.max(0, stamina / maxStamina)
|
|||
|
|
expect(ratio).toBe(0)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('属性默认值处理', () => {
|
|||
|
|
const baseStats = undefined
|
|||
|
|
const dexterity = baseStats?.dexterity || 0
|
|||
|
|
expect(dexterity).toBe(0)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
})
|