deadhead-space-freight-tycoon/apps/web/src/map/reach.ts

56 lines
2.3 KiB
TypeScript

import { content } from "@deadhead/content";
import type { PlayerState, Ship } from "@deadhead/schema";
import { checkRoute, effectiveStats } from "@deadhead/sim";
export interface OriginReach {
originId: string;
/** Purchasable models in range from there, then the player's upgraded ships there. */
reachers: string[];
}
/**
* Where the player could fly from: discovered locations with one of their HQs
* or idle ships (CR-012). Earth when there are none yet (the tutorial).
*/
export function playerOrigins(state: Pick<PlayerState, "bays" | "ships" | "locations">): string[] {
const used = new Set([
...state.bays.map((b) => b.locationId),
...state.ships.flatMap((s) => (s.locationId ? [s.locationId] : [])),
]);
const origins = state.locations.filter((l) => l.discovered && used.has(l.id)).map((l) => l.id);
return origins.length > 0 ? origins : ["earth"];
}
/**
* Who can fly to `locationId` right now, per origin (Steps 10A.16, 10L.8):
* purchasable models whose range from that origin covers the leg (the sim's
* route check, with the origin's range factor), and the player's ships at that
* origin that get there only thanks to their upgrades (CR-009). Origins with
* nobody are left out.
*/
export function reachersByOrigin(
state: Pick<PlayerState, "bays" | "ships" | "locations">,
locationId: string,
): OriginReach[] {
const target = content.location(locationId);
if (!target) return [];
return playerOrigins(state)
.filter((id) => id !== locationId)
.map((originId) => {
const origin = content.location(originId)!;
const reaches = (stats: Parameters<typeof checkRoute>[0]) =>
checkRoute(stats, origin, target, content.config).ok;
const models = content.purchasableShipModels.filter(reaches).map((m) => m.name);
const upgraded = state.ships
.filter((ship: Ship) => {
const model = content.shipModel(ship.modelId);
if (ship.locationId !== originId || !model?.purchasable) return false;
if (Object.keys(ship.upgrades).length === 0 || reaches(model)) return false;
return reaches(effectiveStats(model, ship.upgrades, content));
})
.map((ship) => `your ${ship.name} (upgraded)`);
return { originId, reachers: [...models, ...upgraded] };
})
.filter((o) => o.reachers.length > 0);
}