SOURCE / PINNED RELEASE
Made of little things.
Poly Dogfight
- Release
- b8511ee374e3…
- Author-recorded commit
- 5579434b9d8b…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-44f63e5422b
Archive hash verified: bcadd2430d8ea2bc…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Local multiplayer: controller roster, split-screen layout and mid-air collisions.
import assert from 'node:assert/strict';
import test from 'node:test';
import { Vector3 } from 'three';
import { mergeControls } from '../src/input.ts';
import { MAX_PLAYERS, planRoster, screenIndicator, splitLayout, steerToward } from '../src/players.ts';
import { PLANET_RADIUS } from '../src/sim/constants.ts';
import { collidePlanes, createFlightState, neutralInput, spawnAt, stepFlight } from '../src/sim/flight.ts';
const pads = (plan) => plan.map((slot) => slot.pad);
test('one controller flies pilot 1 alongside the keyboard', () => {
assert.deepEqual(planRoster([null], []), [{ from: 0, pad: null }]);
assert.deepEqual(planRoster([null], [0]), [{ from: 0, pad: 0 }]);
});
test('each extra controller adds a pilot, up to four', () => {
assert.deepEqual(pads(planRoster([0], [0, 1])), [0, 1]);
assert.deepEqual(pads(planRoster([null], [0, 1, 2])), [0, 1, 2]);
const full = planRoster([0, 1, 2, 3], [0, 1, 2, 3, 4]);
assert.equal(full.length, MAX_PLAYERS);
assert.deepEqual(pads(full), [0, 1, 2, 3]);
});
test('a pilot leaves when their controller disconnects; pilot 1 stays', () => {
const plan = planRoster([0, 1, 2], [0, 2]);
assert.deepEqual(plan, [
{ from: 0, pad: 0 },
{ from: 2, pad: 2 },
]);
assert.deepEqual(planRoster([0, 1], [1]), [
{ from: 0, pad: null },
{ from: 1, pad: 1 },
]);
// A returning controller takes pilot 1's empty seat rather than adding a pilot.
assert.deepEqual(pads(planRoster([null, 1], [0, 1])), [0, 1]);
});
test('split layout covers the frame without overlap', () => {
for (const [w, h] of [[1600, 900], [900, 600], [320, 560], [200, 160]]) {
for (let count = 1; count <= 4; count++) {
const { views, spare } = splitLayout(count, w, h);
assert.equal(views.length, count);
const rects = spare ? [...views, spare] : views;
const area = rects.reduce((sum, r) => sum + r.w * r.h, 0);
assert.ok(Math.abs(area - 1) < 1e-9, `${count} pilots at ${w}×${h} cover ${area}`);
}
}
// Two pilots: side by side on a wide frame, stacked on a tall one.
assert.equal(splitLayout(2, 1600, 900).views[1].x, 0.5);
assert.equal(splitLayout(2, 320, 560).views[1].y, 0.5);
});
test('keyboard and controller input merge with the strongest axis winning', () => {
const merged = mergeControls(
{ ...neutralInput(), pitch: 0.3, boost: true },
{ ...neutralInput(), pitch: -0.8, roll: 0.5 },
);
assert.equal(merged.pitch, -0.8);
assert.equal(merged.roll, 0.5);
assert.equal(merged.boost, true);
});
const sky = { surfaceHeight: () => 0, terrainHeight: () => -60, hitsObstacle: () => false };
function planeAt(angle, heading) {
const state = createFlightState();
spawnAt(state, sky, new Vector3(Math.sin(angle), Math.cos(angle), 0), heading);
state.position.setLength(PLANET_RADIUS + 150);
return state;
}
test('two planes flying head-on collide and both crash', () => {
const gap = 200 / (PLANET_RADIUS + 150);
const a = planeAt(-gap / 2, new Vector3(1, 0, 0));
const b = planeAt(gap / 2, new Vector3(-1, 0, 0));
let hits = [];
for (let i = 0; i < 6 * 120 && !hits.length; i++) {
stepFlight(a, neutralInput(), sky, 1 / 120);
stepFlight(b, neutralInput(), sky, 1 / 120);
hits = collidePlanes([a, b]);
}
assert.deepEqual(hits, [[0, 1]]);
assert.equal(a.alive, false);
assert.equal(b.alive, false);
assert.equal(a.crashCause, 'midair');
assert.ok(a.crashTimer > 4.9);
});
test('planes in formation do not collide, and wrecks are ignored', () => {
const a = planeAt(0, new Vector3(0, 0, -1));
const b = planeAt(0, new Vector3(0, 0, -1));
b.position.x += 30;
assert.deepEqual(collidePlanes([a, b]), []);
b.position.copy(a.position);
b.alive = false;
assert.deepEqual(collidePlanes([a, b]), []);
assert.equal(a.alive, true);
});
test('a pilot in view gets a tag at their projected position', () => {
const spot = screenIndicator({ x: 0, y: 0, z: -100 }, 1, 1, 800, 400, 16);
assert.equal(spot.onScreen, true);
assert.deepEqual([spot.x, spot.y], [400, 200]);
const upRight = screenIndicator({ x: 50, y: 50, z: -100 }, 1, 1, 800, 400, 16);
assert.equal(upRight.onScreen, true);
assert.deepEqual([upRight.x, upRight.y], [600, 100]);
});
test('a pilot out of view is pinned to the edge on the side to turn toward', () => {
const right = screenIndicator({ x: 500, y: 0, z: -100 }, 1, 1, 800, 400, 16);
assert.equal(right.onScreen, false);
assert.deepEqual([right.x, right.y], [784, 200]);
assert.ok(Math.abs(right.angle) < 1e-12);
// Behind and to the left still points left, not mirrored to the right.
const behindLeft = screenIndicator({ x: -30, y: 0, z: 100 }, 1, 1, 800, 400, 16);
assert.equal(behindLeft.onScreen, false);
assert.equal(behindLeft.x, 16);
// Above the view points up; dead astern points down ("turn around").
const above = screenIndicator({ x: 0, y: 300, z: -100 }, 1, 1, 800, 400, 16);
assert.deepEqual([above.x, above.y], [400, 16]);
const astern = screenIndicator({ x: 0, y: 0, z: 100 }, 1, 1, 800, 400, 16);
assert.deepEqual([astern.x, astern.y], [400, 384]);
// Every edge position stays inside the view.
for (let i = 0; i < 64; i++) {
const a = (i / 64) * Math.PI * 2;
const s = screenIndicator({ x: Math.cos(a) * 900, y: Math.sin(a) * 900, z: 50 }, 1.2, 0.7, 320, 180, 16);
assert.ok(s.x >= 16 - 1e-9 && s.x <= 304 + 1e-9 && s.y >= 16 - 1e-9 && s.y <= 164 + 1e-9);
}
});
test('far-away pilots are found along the horizon, not through the planet', () => {
const R = PLANET_RADIUS + 100;
const near = steerToward({ x: 0, y: R, z: 0 }, { x: 60, y: R, z: 0 });
assert.deepEqual(near.aim, { x: 60, y: R, z: 0 });
// A quarter of the way round: aim a short way along the great circle, at altitude.
const far = steerToward({ x: 0, y: R, z: 0 }, { x: R, y: 0, z: 0 });
assert.ok(Math.abs(Math.hypot(far.aim.x, far.aim.y, far.aim.z) - R) < 1e-6);
assert.ok(far.aim.x > 0 && Math.abs(far.aim.z) < 1e-9);
assert.ok(Math.abs(far.distance - (Math.PI / 2) * R) < 1e-6);
// The far side of the planet still yields a usable aim point above the ground.
const opposite = steerToward({ x: 0, y: R, z: 0 }, { x: 0, y: -R, z: 0 });
assert.ok(Math.hypot(opposite.aim.x, opposite.aim.y, opposite.aim.z) > PLANET_RADIUS);
assert.ok(Math.abs(opposite.distance - Math.PI * R) < 1e-6);
});
