56 lines
2.3 KiB
TypeScript
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);
|
|
}
|