deadhead-space-freight-tycoon/apps/web/test/map-scene.test.ts

105 lines
3.3 KiB
TypeScript

import type { Dispatch } from "@deadhead/schema";
import { describe, expect, it } from "vitest";
import { DEFAULT_CAMERA, toScreen, zoomAt } from "../src/map/camera";
import { buildScene, flightMarker } from "../src/map/scene";
import { samplePlayerState, shipExtras } from "./render";
const NOW = Date.parse("2100-01-01T00:00:00Z");
const flight: Dispatch = {
id: "d1",
shipId: "s1",
pilotId: "p1",
contract: null,
originId: "earth",
destinationId: "mars",
tutorial: false,
departureAt: NOW - 3_600_000,
arrivalAt: NOW + 3_600_000,
distanceMillionKm: 150,
fuelCost: 0,
expectedPayout: 0,
seed: 1,
status: "in-flight",
resolvedAt: null,
};
describe("ship markers", () => {
it("puts a ship at 50% progress on the route line between the endpoints, on screen", () => {
const viewport = { width: 375, height: 690 };
const camera = zoomAt(DEFAULT_CAMERA, 2.5, { x: 100, y: 200 }, viewport);
const scene = flightMarker(flight, NOW, "courier");
expect(scene.progress).toBe(0.5);
const a = toScreen(scene.from, viewport, camera);
const b = toScreen(scene.to, viewport, camera);
const m = toScreen(scene.marker, viewport, camera);
// On the line: the cross product of (b - a) and (m - a) is zero.
const cross = (b.x - a.x) * (m.y - a.y) - (b.y - a.y) * (m.x - a.x);
expect(Math.abs(cross)).toBeLessThan(1e-6);
// Between the endpoints, halfway along.
expect(Math.hypot(m.x - a.x, m.y - a.y)).toBeCloseTo(Math.hypot(b.x - a.x, b.y - a.y) / 2, 6);
});
it("starts at the origin and ends at the destination", () => {
expect(flightMarker(flight, flight.departureAt, "courier").marker).toEqual(
flightMarker(flight, NOW, "courier").from,
);
const end = flightMarker(flight, flight.arrivalAt + 60_000, "courier");
expect(end.marker.x).toBeCloseTo(end.to.x, 12);
expect(end.marker.y).toBeCloseTo(end.to.y, 12);
});
it("builds one flight per active dispatch and badges idle ships at their location", () => {
const state = samplePlayerState({
tutorial: null,
activeDispatches: [flight],
ships: [
{
id: "s1",
modelId: "courier",
name: "A",
status: "in-flight",
locationId: null,
dispatchId: "d1",
lastArrivalAt: 0,
...shipExtras("courier"),
pilotId: "p1",
},
{
id: "s2",
modelId: "courier",
name: "B",
status: "idle",
locationId: "moon",
dispatchId: null,
lastArrivalAt: 0,
...shipExtras("courier"),
pilotId: null,
},
{
id: "s3",
modelId: "courier",
name: "C",
status: "idle",
locationId: "moon",
dispatchId: null,
lastArrivalAt: 0,
...shipExtras("courier"),
pilotId: null,
},
],
});
const scene = buildScene(state, NOW);
expect(scene.flights.map((f) => f.dispatchId)).toEqual(["d1"]);
expect(scene.locations.find((l) => l.id === "moon")!.idleShips).toBe(2);
expect(scene.locations.find((l) => l.id === "earth")!.idleShips).toBe(0);
});
it("counts the idle tutorial loaner at its location", () => {
const scene = buildScene(samplePlayerState(), NOW);
expect(scene.locations.find((l) => l.id === "earth")!.idleShips).toBe(1);
});
});