106 lines
3.4 KiB
TypeScript
106 lines
3.4 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,
|
|
fuelBurnedFu: 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);
|
|
});
|
|
});
|