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.
import test from 'node:test';
import assert from 'node:assert/strict';
import { Vector3 } from 'three';
import { findAim, gunMuzzle, interceptTime } from '../src/sim/aim.ts';
import { createFlightState } from '../src/sim/flight.ts';
import { GUNS } from '../src/sim/constants.ts';
import { createGuns, stepGuns } from '../src/sim/guns.ts';
const sky = { surfaceHeight: () => 0, terrainHeight: () => -60, hitsObstacle: () => false };
function setup(x = 4) {
const shooter = createFlightState();
shooter.alive = true; shooter.position.set(0, 800, 0); shooter.velocity.set(0, 0, -60);
const target = createFlightState();
target.alive = true; target.position.set(x, 800, -100); target.velocity.set(0, 0, -60);
return { shooter, target };
}
test('intercept solves transverse motion and rejects impossible pursuit', () => {
const r = new Vector3(0, 0, -100), v = new Vector3(60, 0, 0);
const t = interceptTime(r, v, 300);
assert.ok(Math.abs(r.clone().addScaledVector(v, t).length() - 300 * t) < 1e-8);
assert.equal(interceptTime(r, new Vector3(0, 0, -400), 300), null);
assert.equal(interceptTime(r, new Vector3(0, 0, 300), 300), 1 / 6);
});
test('assisted direction meets moving hit point and never exceeds five degrees', () => {
const { shooter, target } = setup(); target.velocity.x = 8;
const aim = findAim(shooter, [shooter, target], sky);
assert.ok(aim && aim.angle <= GUNS.aimAssistAngle);
const actual = gunMuzzle(shooter).addScaledVector(shooter.velocity, aim.time).addScaledVector(aim.direction, GUNS.muzzleSpeed * aim.time);
assert.ok(actual.distanceTo(aim.point) < 1e-7);
});
test('outside cone, dead, behind, expired and obstructed targets receive no assistance', () => {
const { shooter, target } = setup(20);
assert.equal(findAim(shooter, [shooter, target], sky), null);
target.position.x = 0; target.alive = false;
assert.equal(findAim(shooter, [shooter, target], sky), null);
target.alive = true; target.position.z = 100;
assert.equal(findAim(shooter, [shooter, target], sky), null);
target.position.z = -600;
assert.equal(findAim(shooter, [shooter, target], sky), null);
target.position.z = -100;
assert.equal(findAim(shooter, [shooter, target], { ...sky, hitsObstacle: p => p.z < -40 && p.z > -45 }), null);
assert.equal(findAim(shooter, [shooter, target], { ...sky, surfaceHeight: () => 1000 }), null);
});
test('least correction wins regardless of roster order', () => {
const { shooter, target } = setup(7);
const closer = setup(2).target;
assert.equal(findAim(shooter, [shooter, target, closer], sky).target, closer);
assert.equal(findAim(shooter, [closer, shooter, target], sky).target, closer);
});
test('moving targets are hit and only the shooter receives confirmation', () => {
const { shooter, target } = setup(); target.velocity.x = 8;
const guns = createGuns(); let confirmations = [], hits = [];
for (let i = 0; i < 120; i++) {
const events = stepGuns(guns, [shooter, target], [i === 0, false], sky, 1 / 120);
confirmations.push(...events.confirmations); hits.push(...events.hits);
shooter.position.addScaledVector(shooter.velocity, 1 / 120);
target.position.addScaledVector(target.velocity, 1 / 120);
}
assert.equal(hits.length, 1); assert.equal(confirmations.length, 1);
assert.equal(hits[0].owner, shooter); assert.equal(confirmations[0].owner, shooter);
assert.equal(confirmations[0].armored, false);
});
test('bullets do not home when an opponent dodges after firing', () => {
const { shooter, target } = setup(); const guns = createGuns();
stepGuns(guns, [shooter, target], [true, false], sky, 1 / 120);
const velocity = guns.bullets[0].velocity.clone(); target.velocity.x = 200;
let hits = 0;
for (let i = 0; i < 100; i++) {
target.position.addScaledVector(target.velocity, 1 / 120);
hits += stepGuns(guns, [shooter, target], [false, false], sky, 1 / 120).hits.length;
if (guns.bullets.length) assert.ok(guns.bullets[0].velocity.equals(velocity));
}
assert.equal(hits, 0);
});
test('armor contact reports distinct shooter feedback without damage', () => {
const { shooter, target } = setup(0); const guns = createGuns();
guns.bullets.push({ owner: shooter, position: target.position.clone().add(new Vector3(0, .3, -3)), velocity: new Vector3(0, 0, 300), age: 0 });
const events = stepGuns(guns, [shooter, target], [false, false], sky, 1 / 120);
assert.equal(events.hits.length, 0);
assert.deepEqual(events.confirmations, [{ owner: shooter, armored: true }]);
});
test('rear and far-side targets are filtered before any terrain or obstacle work', () => {
const { shooter, target } = setup(200); let checks = 0;
const world = { ...sky, surfaceHeight: () => { checks++; return 0; }, hitsObstacle: () => { checks++; return false; } };
assert.equal(findAim(shooter, [shooter, target], world), null);
target.position.set(0, 800, 100);
assert.equal(findAim(shooter, [shooter, target], world), null);
assert.equal(checks, 0);
});
