airship-phaser/tests/game-logic.test.mjs

118 lines
3.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
DEFAULT_PIRATE_SHOT_DAMAGE,
PLAYER_MAX_HULL,
normalizePlayerDamage,
reducePlayerProjectileHits,
resolveTaggedProjectile,
} from '../src/game-logic.js';
test('first pirate projectile removes exactly the default damage', () => {
const result = reducePlayerProjectileHits({
hull: PLAYER_MAX_HULL,
hits: [{ damage: DEFAULT_PIRATE_SHOT_DAMAGE }],
});
assert.equal(result.hull, 94);
assert.equal(result.appliedHits, 1);
assert.equal(result.totalDamage, 6);
assert.equal(result.gameOver, false);
});
test('damage normalization handles invalid and extreme values safely', () => {
assert.equal(normalizePlayerDamage(undefined), 6);
assert.equal(normalizePlayerDamage('not-a-number'), 6);
assert.equal(normalizePlayerDamage(999), 25);
assert.equal(normalizePlayerDamage(-12), 1);
assert.equal(normalizePlayerDamage('8'), 8);
});
test('invalid hull state recovers from max hull before applying projectile damage', () => {
const result = reducePlayerProjectileHits({
hull: Number.NaN,
hits: [{ damage: 6 }],
});
assert.equal(result.hull, 94);
assert.equal(result.gameOver, false);
});
test('lethal projectile damage clamps hull at zero and reports game over', () => {
const result = reducePlayerProjectileHits({
hull: 5,
hits: [{ damage: 6 }],
});
assert.equal(result.hull, 0);
assert.equal(result.totalDamage, 6);
assert.equal(result.gameOver, true);
});
test('empty projectile queue is a no-op', () => {
const result = reducePlayerProjectileHits({
hull: 72,
hits: [],
});
assert.equal(result.hull, 72);
assert.equal(result.totalDamage, 0);
assert.equal(result.appliedHits, 0);
assert.equal(result.gameOver, false);
});
test('every distinct pirate projectile can damage once with no cross-shot suppression', () => {
const first = reducePlayerProjectileHits({
hull: 100,
hits: [{ damage: 6 }],
});
const second = reducePlayerProjectileHits({
hull: first.hull,
hits: [{ damage: 6 }],
});
assert.equal(first.hull, 94);
assert.equal(second.hull, 88);
});
test('multiple distinct pirate projectiles in the same frame all apply damage', () => {
const result = reducePlayerProjectileHits({
hull: 100,
hits: [
{ damage: 6 },
{ damage: 6 },
{ damage: 6 },
],
});
assert.equal(result.hull, 82);
assert.equal(result.totalDamage, 18);
assert.equal(result.appliedHits, 3);
});
test('projectile resolver is independent of Phaser overlap argument order', () => {
const player = {
getData() { return undefined; },
};
const pirateShot = {
getData(key) {
return key === 'projectileOwner' ? 'pirate' : undefined;
},
};
assert.equal(resolveTaggedProjectile(pirateShot, player, 'pirate'), pirateShot);
assert.equal(resolveTaggedProjectile(player, pirateShot, 'pirate'), pirateShot);
});
test('projectile resolver never mistakes the player for an untagged shot', () => {
const player = {
getData() { return undefined; },
};
const untagged = {
getData() { return undefined; },
};
assert.equal(resolveTaggedProjectile(player, untagged, 'pirate'), null);
});