First commit, phased 1 and 2 created but untested
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user