feat: v0.8.0 — player-actions API: accept/dispatch/buy/sell/refuel endpoints (tick-first, full-snapshot responses), spawn coverage rule, test create-contract hook

This commit is contained in:
2026-09-17 13:54:13 +00:00
parent 96835e935d
commit aa70eb5286
25 changed files with 1843 additions and 8 deletions
+24 -3
View File
@@ -98,19 +98,40 @@ export function rewardForDistanceKm(km: number): number {
// deadline now + 6–72h game time (or now + deadlineOffsetMs when given — used by
// the e2e test hook to force a near-term expiry). The RNG call order per contract
// is fixed: origin, dest, cargoType, cargoCount, [deadline window].
// v0.8.0: originIcao/destIcao optionally FORCE an endpoint (spawn coverage rule +
// the e2e create-contract hook); a forced endpoint consumes no RNG draws, so
// callers that omit both get byte-identical behavior to before.
export function generateContract(input: {
id: string;
airports: SeedAirport[];
rng: Rng;
nowMs: number;
deadlineOffsetMs?: number;
originIcao?: string;
destIcao?: string;
}): SeedContract {
const { airports, rng, nowMs } = input;
if (airports.length < 2) throw new Error('contract generation needs at least two airports');
const oi = Math.floor(rng() * airports.length);
let di = Math.floor(rng() * airports.length);
if (di === oi) di = (di + 1) % airports.length;
let oi: number;
if (input.originIcao !== undefined) {
oi = airports.findIndex((a) => a.icao === input.originIcao);
if (oi < 0) throw new Error(`unknown origin airport ${input.originIcao}`);
} else {
oi = Math.floor(rng() * airports.length);
}
let di: number;
if (input.destIcao !== undefined) {
di = airports.findIndex((a) => a.icao === input.destIcao);
if (di < 0) throw new Error(`unknown destination airport ${input.destIcao}`);
// A RANDOM origin may have landed on the forced destination — shift it (no
// extra RNG draw), mirroring the unforced collision handling below.
if (oi === di && input.originIcao === undefined) oi = (oi + 1) % airports.length;
} else {
di = Math.floor(rng() * airports.length);
if (di === oi) di = (di + 1) % airports.length;
}
if (oi === di) throw new Error('origin and destination must differ'); // both forced identical
const origin = airports[oi];
const dest = airports[di];