airship-phaser/tests/environment-physics.test.mjs

136 lines
4.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
ENVIRONMENT_ISLANDS,
environmentWindAt,
routeWeatherAt,
semiRandomWindPattern,
stormFrontAt,
velocityWithEnvironment,
} from '../src/environment-physics.js';
test('environment exposes collidable island geometry', () => {
assert.equal(ENVIRONMENT_ISLANDS.length, 2);
for (const island of ENVIRONMENT_ISLANDS) {
assert.ok(island.radius > 0);
assert.ok(island.turbulenceRadius > island.radius);
}
});
test('semi-random wind varies strength and direction across weather windows', () => {
const samples = [0, 16000, 32000, 48000, 64000]
.map((timeMs) => semiRandomWindPattern(timeMs));
const roundedStrengths = new Set(samples.map(({ strength }) => Math.round(strength * 10)));
const roundedHeadings = new Set(samples.map(({ angle }) => Math.round(angle * 10)));
assert.ok(roundedStrengths.size >= 3);
assert.ok(roundedHeadings.size >= 3);
for (const sample of samples) {
assert.ok(sample.strength >= 4 && sample.strength < 28);
}
});
test('semi-random wind changes smoothly instead of jumping each frame', () => {
const before = semiRandomWindPattern(15950);
const after = semiRandomWindPattern(16050);
const vector = ({ angle, strength }) => ({
x: Math.cos(angle) * strength,
y: Math.sin(angle) * strength,
});
const a = vector(before);
const b = vector(after);
assert.ok(Math.hypot(b.x - a.x, b.y - a.y) < 2.5);
});
test('ambient wind changes over time and remains bounded', () => {
const first = environmentWindAt({ x: 1200, y: 1200, timeMs: 0 });
const later = environmentWindAt({ x: 1200, y: 1200, timeMs: 24000 });
assert.ok(first.strength > 3 && first.strength < 55);
assert.ok(later.strength > 3 && later.strength < 55);
assert.notDeepEqual(
[Math.round(first.x * 100), Math.round(first.y * 100)],
[Math.round(later.x * 100), Math.round(later.y * 100)],
);
});
test('islands produce a local turbulence contribution', () => {
const island = ENVIRONMENT_ISLANDS[0];
const near = environmentWindAt({
x: island.x + island.radius + 20,
y: island.y,
timeMs: 6000,
});
const far = environmentWindAt({
x: island.x + island.turbulenceRadius + 300,
y: island.y,
timeMs: 6000,
});
assert.notEqual(
Math.round(near.strength * 100),
Math.round(far.strength * 100),
);
});
test('environment velocity adds wind according to exposure', () => {
const result = velocityWithEnvironment(100, -20, { x: 12, y: 8 }, 0.5);
assert.equal(result.x, 106);
assert.equal(result.y, -16);
});
test('shipping lanes create calm cores with unstable edges and open sky beyond', () => {
const routes = [{
id: 'shipping-1',
from: { x: 1000, y: 1200 },
to: { x: 3600, y: 1200 },
width: 220,
}];
const lane = routeWeatherAt({ x: 2200, y: 1200, routes });
const edge = routeWeatherAt({ x: 2200, y: 1500, routes });
const open = routeWeatherAt({ x: 2200, y: 2200, routes });
assert.equal(lane.zone, 'shipping-lane');
assert.ok(lane.routeInfluence > 0.9);
assert.equal(edge.zone, 'route-edge');
assert.ok(edge.routeEdge > lane.routeEdge);
assert.equal(open.zone, 'open-sky');
assert.ok(open.routeInfluence < 0.05);
});
test('moving storm fronts can cross the world independent of route corridors', () => {
const timeMs = 110000 * 0.25;
const intensity = stormFrontAt({
x: 900,
y: 5400 * 0.53,
timeMs,
worldWidth: 5400,
worldHeight: 5400,
});
assert.ok(intensity > 0.75);
});
test('route-aware wind is calmer in a shipping lane than nearby open sky', () => {
const routes = [{
id: 'shipping-1',
from: { x: 1000, y: 1000 },
to: { x: 3600, y: 1000 },
width: 220,
}];
const lane = environmentWindAt({
x: 2200, y: 1000, timeMs: 0, routes, islands: [],
});
const open = environmentWindAt({
x: 2200, y: 2200, timeMs: 0, routes, islands: [],
});
assert.equal(lane.zone, 'shipping-lane');
assert.equal(open.zone, 'open-sky');
assert.ok(lane.strength < open.strength);
});