79 lines
2.6 KiB
JavaScript
79 lines
2.6 KiB
JavaScript
import * as THREE from 'three';
|
|
import { createPhysicsWorld, initPhysics } from '../src/physics/world.js';
|
|
import { createMatch } from '../src/game/match.js';
|
|
import { describeHit } from '../src/game/hits.js';
|
|
|
|
/**
|
|
* Fire skaters at each other from various run-ups and angles and print what
|
|
* comes out. A tuning aid, not a test: the numbers below are the ones you stare
|
|
* at when deciding what should count as a bump, a stagger and a knockdown.
|
|
*
|
|
* node tools/hitprobe.mjs
|
|
*/
|
|
|
|
const DT = 1 / 60;
|
|
await initPhysics();
|
|
|
|
/**
|
|
* @param {'stationary'|'full'} mode is the victim skating into it too
|
|
* @param {number} gap metres between them at the start
|
|
* @param {number} offsetZ lateral offset — 0 is dead centre
|
|
*/
|
|
function probe(mode, gap, offsetZ = 0) {
|
|
const physics = createPhysicsWorld();
|
|
const match = createMatch({ scene: new THREE.Group(), physics, perTeam: 1, teams: 2 });
|
|
const [a, b] = match.states;
|
|
a.x = -gap / 2; a.z = 0; a.yaw = Math.PI / 2;
|
|
b.x = gap / 2; b.z = offsetZ; b.yaw = mode === 'full' ? -Math.PI / 2 : Math.PI / 2;
|
|
match.skaters[0].proxy.teleport(a.x, a.z);
|
|
match.skaters[1].proxy.teleport(b.x, b.z);
|
|
match.setControl(0, { x: 1, y: 0, sprint: true, brake: false, cameraYaw: 0 });
|
|
match.setControl(1, mode === 'full'
|
|
? { x: -1, y: 0, sprint: true, brake: false, cameraYaw: 0 }
|
|
: { x: 0, y: 0, sprint: false, brake: false, cameraYaw: 0 });
|
|
|
|
const seen = new Set();
|
|
const out = [];
|
|
for (let n = 0; n < 6 / DT; n++) {
|
|
match.update(DT);
|
|
for (const h of match.recentHits) {
|
|
const id = `${h.at}|${h.attacker}`;
|
|
if (!seen.has(id)) {
|
|
seen.add(id);
|
|
out.push(h);
|
|
}
|
|
}
|
|
}
|
|
physics.destroy();
|
|
return out;
|
|
}
|
|
|
|
const rows = [
|
|
['stationary', 2.5, 0],
|
|
['stationary', 5, 0],
|
|
['stationary', 10, 0],
|
|
['stationary', 22, 0],
|
|
['stationary', 22, 0.45],
|
|
['stationary', 22, -0.45],
|
|
['full', 10, 0],
|
|
['full', 24, 0],
|
|
['full', 24, 0.5],
|
|
];
|
|
|
|
console.log('mode gap offZ | outcome m/s sev limbs description');
|
|
console.log('-'.repeat(96));
|
|
for (const [mode, gap, off] of rows) {
|
|
const hits = probe(mode, gap, off);
|
|
if (!hits.length) {
|
|
console.log(`${mode.padEnd(11)} ${String(gap).padStart(4)} ${String(off).padStart(5)} | (no hit)`);
|
|
continue;
|
|
}
|
|
for (const h of hits) {
|
|
console.log(
|
|
`${mode.padEnd(11)} ${String(gap).padStart(4)} ${String(off).padStart(5)} | `
|
|
+ `${h.outcome.padEnd(10)} ${h.speed.toFixed(1).padStart(4)} ${h.severity.toFixed(1).padStart(5)} `
|
|
+ `${(h.attackerPart + '→' + h.victimPart).padEnd(24)} ${describeHit(h)}`,
|
|
);
|
|
}
|
|
}
|