139 lines
4.5 KiB
JavaScript
139 lines
4.5 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { calcDerived, computeEnergy, xpToNext, checkLevelUp, ENERGY_MAX } = require('../../src/game/stats');
|
|
|
|
// calcDerived tests
|
|
test('calcDerived warrior at level 1 with no equipment', () => {
|
|
const derived = calcDerived({ str: 8, agi: 4, int: 2, vit: 8 }, 1, 'warrior');
|
|
// maxHp = 50 + 8*10 + 1*5 = 135
|
|
assert.equal(derived.maxHp, 135);
|
|
// primary = str = 8, attack = 5 + 8*2 + 1 = 22
|
|
assert.equal(derived.attack, 22);
|
|
// defense = floor(8*1.5) = 12, +10% warrior = 13
|
|
assert.equal(derived.defense, 13);
|
|
// speed = 10 + 4 = 14
|
|
assert.equal(derived.speed, 14);
|
|
// critChance = min(40, 5 + floor(4*0.5)) = min(40, 7) = 7
|
|
assert.equal(derived.critChance, 7);
|
|
});
|
|
|
|
test('calcDerived mage at level 1 with no equipment', () => {
|
|
const derived = calcDerived({ str: 2, agi: 4, int: 9, vit: 5 }, 1, 'mage');
|
|
// maxHp = 50 + 5*10 + 1*5 = 105
|
|
assert.equal(derived.maxHp, 105);
|
|
// primary = int = 9, attack = 5 + 9*2 + 1 = 24
|
|
assert.equal(derived.attack, 24);
|
|
// defense = floor(5*1.5) = 7 (no warrior bonus)
|
|
assert.equal(derived.defense, 7);
|
|
// speed = 10 + 4 = 14
|
|
assert.equal(derived.speed, 14);
|
|
// critChance = min(40, 5 + floor(4*0.5)) = 7
|
|
assert.equal(derived.critChance, 7);
|
|
});
|
|
|
|
test('calcDerived ranger at level 1 with no equipment', () => {
|
|
const derived = calcDerived({ str: 5, agi: 9, int: 3, vit: 5 }, 1, 'ranger');
|
|
// maxHp = 50 + 5*10 + 1*5 = 105
|
|
assert.equal(derived.maxHp, 105);
|
|
// primary = agi = 9, attack = 5 + 9*2 + 1 = 24
|
|
assert.equal(derived.attack, 24);
|
|
// defense = floor(5*1.5) = 7
|
|
assert.equal(derived.defense, 7);
|
|
// speed = 10 + 9 = 19
|
|
assert.equal(derived.speed, 19);
|
|
// critChance = min(40, 5 + floor(9*0.5)) + 8 = min(40, 9) + 8 = 17
|
|
assert.equal(derived.critChance, 17);
|
|
});
|
|
|
|
test('calcDerived with equipment bonuses', () => {
|
|
const derived = calcDerived(
|
|
{ str: 8, agi: 4, int: 2, vit: 8 },
|
|
1,
|
|
'warrior',
|
|
{ bonus_str: 2, bonus_attack: 5, bonus_defense: 3 }
|
|
);
|
|
// totalStr = 10, attack = 5 + 10*2 + 1 + 5 = 31
|
|
assert.equal(derived.attack, 31);
|
|
// totalVit = 8, defense = floor(8*1.5) = 12, +3 equip = 15, +10% = 16
|
|
assert.equal(derived.defense, 16);
|
|
});
|
|
|
|
// computeEnergy tests
|
|
test('computeEnergy no time passed', () => {
|
|
const result = computeEnergy(50, Date.now(), Date.now() - 1000);
|
|
assert.equal(result.energy, 50);
|
|
});
|
|
|
|
test('computeEnergy partial tick (less than 1 regen interval)', () => {
|
|
const now = Date.now();
|
|
const result = computeEnergy(50, now - 10000, now);
|
|
// 10s < 180s → no regen
|
|
assert.equal(result.energy, 50);
|
|
});
|
|
|
|
test('computeEnergy full tick', () => {
|
|
const now = Date.now();
|
|
const result = computeEnergy(50, now - 360000, now);
|
|
// 360s / 180s = 2 ticks
|
|
assert.equal(result.energy, 52);
|
|
});
|
|
|
|
test('computeEnergy caps at max', () => {
|
|
const now = Date.now();
|
|
const result = computeEnergy(100, now - 3600000, now);
|
|
assert.equal(result.energy, ENERGY_MAX);
|
|
});
|
|
|
|
test('computeEnergy clock going backwards', () => {
|
|
const result = computeEnergy(50, Date.now(), Date.now() - 100000);
|
|
assert.equal(result.energy, 50);
|
|
});
|
|
|
|
test('computeEnergy large gap', () => {
|
|
const now = Date.now();
|
|
const result = computeEnergy(30, now - 36000000, now); // 10 hours ago
|
|
assert.equal(result.energy, ENERGY_MAX);
|
|
});
|
|
|
|
// xpToNext tests
|
|
test('xpToNext level 1→2 is 100', () => {
|
|
assert.equal(xpToNext(1), 100);
|
|
});
|
|
|
|
test('xpToNext level 2→3 is 282', () => {
|
|
assert.equal(xpToNext(2), 282);
|
|
});
|
|
|
|
test('xpToNext level 5→6 is 1118', () => {
|
|
assert.equal(xpToNext(5), 1118);
|
|
});
|
|
|
|
// checkLevelUp tests
|
|
test('checkLevelUp no level up', () => {
|
|
const result = checkLevelUp(1, 50, 10);
|
|
assert.equal(result.newLevel, 1);
|
|
assert.equal(result.leveledUp, false);
|
|
assert.equal(result.totalXp, 60);
|
|
});
|
|
|
|
test('checkLevelUp exact threshold', () => {
|
|
const result = checkLevelUp(1, 90, 10);
|
|
assert.equal(result.newLevel, 2);
|
|
assert.equal(result.leveledUp, true);
|
|
assert.equal(result.totalXp, 0);
|
|
});
|
|
|
|
test('checkLevelUp multiple level ups', () => {
|
|
// L1→2 costs 100, L2→3 costs 282. Total 382 for 2 levels. 500 XP → 2 levels, 118 remaining.
|
|
const result = checkLevelUp(1, 0, 500);
|
|
assert.equal(result.newLevel, 3);
|
|
assert.equal(result.leveledUp, true);
|
|
assert.equal(result.totalXp, 118);
|
|
});
|
|
|
|
test('checkLevelUp grants 3 points per level', () => {
|
|
const result = checkLevelUp(1, 0, 282); // exactly enough for L1→L2
|
|
assert.equal(result.newLevel, 2);
|
|
assert.equal(result.unspentPoints, 3);
|
|
});
|