import type { Location, ShipModel } from "@deadhead/content"; import { buildContract, standardCargo, type SimContent } from "./board"; import type { Contract } from "./contract"; /** The tutorial loaner: the content's company ship (decision 0.5.29). */ export function loanerModel(content: SimContent): ShipModel { const id = content.config.tutorialLoanerShipModelId; const loaner = content.shipModels.find((s) => s.id === id); if (!loaner) throw new Error(`Content has no loaner ship model "${id}"`); return loaner; } export type TutorialLocationId = "earth" | "moon"; export interface TutorialBoardInput { /** Where the loaner currently is; contracts start here (decision 0.5.3). */ loanerLocationId: TutorialLocationId; /** Tutorial runs already flown; the board is for run `runsCompleted + 1`. */ runsCompleted: number; periodIndex: number; } function location(id: string, content: SimContent): Location { const found = content.locations.find((l) => l.id === id); if (!found) throw new Error(`Unknown location ${id}`); return found; } /** * The tutorial "company board" (CR-001, Step 10A.3): three Standard contracts * from the loaner's location to the other end of the Earth–Moon route, at the * light, medium and full mass tiers, each paying the tutorial share. A scripted * contract for the upcoming run (decision 0.5.33) replaces the medium slot. * The board is fully determined by its inputs. */ export function generateTutorialBoard(input: TutorialBoardInput, content: SimContent): Contract[] { const { loanerLocationId, runsCompleted, periodIndex } = input; const { config } = content; const origin = location(loanerLocationId, content); const destination = location(loanerLocationId === "earth" ? "moon" : "earth", content); const tiers = config.tutorialMassTonnes; const run = runsCompleted + 1; const scripted = content.scriptedTutorialContracts.find((s) => s.run === run); const slot = (i: number, massTonnes: number): Contract => { const special = i === 1 ? scripted : undefined; const cargoType = special ? content.cargoTypes.find((c) => c.id === special.cargoTypeId) : standardCargo(content); if (!cargoType) throw new Error(`Unknown cargo ${special?.cargoTypeId}`); const contract = buildContract( { id: `tutorial:${loanerLocationId}:${run}:${periodIndex}:${i}`, origin, destination, cargoType, tier: 0, massTonnes, firstContact: false, periodIndex, }, content, ); const share = contract.payout * config.tutorialPayoutShare * (special?.payMultiplier ?? 1); return { ...contract, payout: Math.round(share), ...(special ? { scriptedId: special.id } : {}), }; }; return [slot(0, tiers.light), slot(1, tiers.medium), slot(2, tiers.full)]; }