Step 10J.6: pilot share in the dispatch sheet and delivery summaries
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8cafa50027
commit
f042555502
|
|
@ -1023,3 +1023,8 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
||||||
|
|
||||||
- **Name and bio come from `content.veteranPilot`** in both places; the pilot card shows the bio when the API marks the pilot `isVeteran`.
|
- **Name and bio come from `content.veteranPilot`** in both places; the pilot card shows the bio when the API marks the pilot `isVeteran`.
|
||||||
- **The modal adds a "Pilot" row** to its "You get / Loan / Repayment" list, in the CR's "…and [name]" style, and its opening line mentions the pilot.
|
- **The modal adds a "Pilot" row** to its "You get / Loan / Repayment" list, in the CR's "…and [name]" style, and its opening line mentions the pilot.
|
||||||
|
|
||||||
|
## Step 10J.6
|
||||||
|
|
||||||
|
- **The share uses the sim's `pilotShare`** with the trip's predicted fuel, so the sheet's net profit equals what the server will credit (before any loan repayment).
|
||||||
|
- **`ResolutionSummary.pilotPay`** comes from the stored resolution; older resolutions without it show 0, so no share line.
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ export async function loadPlayerState(
|
||||||
shipper: d.record.contract?.shipper ?? null,
|
shipper: d.record.contract?.shipper ?? null,
|
||||||
credits: d.resolution?.credits ?? 0,
|
credits: d.resolution?.credits ?? 0,
|
||||||
loanRepayment: d.resolution?.loanRepayment ?? 0,
|
loanRepayment: d.resolution?.loanRepayment ?? 0,
|
||||||
|
pilotPay: d.resolution?.pilotPay ?? 0,
|
||||||
pilotXp: d.resolution?.pilotXp ?? 0,
|
pilotXp: d.resolution?.pilotXp ?? 0,
|
||||||
discoveredLocationId: d.resolution?.discoversDestination ? d.record.destinationId : null,
|
discoveredLocationId: d.resolution?.discoversDestination ? d.record.destinationId : null,
|
||||||
scriptedId: d.record.contract?.scriptedId ?? null,
|
scriptedId: d.record.contract?.scriptedId ?? null,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,12 @@ export function AwaySummary() {
|
||||||
<span className="text-violet-300">+{s.pilotXp} XP</span>
|
<span className="text-violet-300">+{s.pilotXp} XP</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{s.pilotPay > 0 && (
|
||||||
|
<div className="mt-1 text-ink-400">
|
||||||
|
{formatCredits(s.pilotPay)} pilot share to{" "}
|
||||||
|
{state.pilots.find((p) => p.id === s.pilotId)?.name ?? "the pilot"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{s.loanRepayment > 0 && (
|
{s.loanRepayment > 0 && (
|
||||||
<div className="mt-1 text-ink-400">
|
<div className="mt-1 text-ink-400">
|
||||||
{formatCredits(s.loanRepayment)} went to the lender
|
{formatCredits(s.loanRepayment)} went to the lender
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,8 @@ export interface PairOption {
|
||||||
problems: string[];
|
problems: string[];
|
||||||
tripMs: number;
|
tripMs: number;
|
||||||
fuelCost: number;
|
fuelCost: number;
|
||||||
|
/** A hired pilot's profit share; 0 for the avatar (CR-010). */
|
||||||
|
pilotPay: number;
|
||||||
netProfit: number;
|
netProfit: number;
|
||||||
arrivalAt: number;
|
arrivalAt: number;
|
||||||
/** The pilot's duty after this trip and their limit (CR-004); null without a pilot. */
|
/** The pilot's duty after this trip and their limit (CR-004); null without a pilot. */
|
||||||
|
|
@ -213,6 +215,7 @@ export function pairOptions(state: PlayerState, contract: Contract, now: number)
|
||||||
problems,
|
problems,
|
||||||
tripMs,
|
tripMs,
|
||||||
fuelCost,
|
fuelCost,
|
||||||
|
pilotPay,
|
||||||
netProfit: contract.payout - fuelCost - pilotPay,
|
netProfit: contract.payout - fuelCost - pilotPay,
|
||||||
arrivalAt: now + tripMs,
|
arrivalAt: now + tripMs,
|
||||||
duty: pilot ? dutyOutlook(pilot, now, now + tripMs) : null,
|
duty: pilot ? dutyOutlook(pilot, now, now + tripMs) : null,
|
||||||
|
|
@ -329,6 +332,10 @@ export function DispatchSheet({ contract, onClose }: { contract: Contract; onClo
|
||||||
<dl className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-3">
|
<dl className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-3">
|
||||||
<Cell label="Trip" value={formatDuration(option.tripMs)} />
|
<Cell label="Trip" value={formatDuration(option.tripMs)} />
|
||||||
<Cell label="Fuel" value={formatCredits(option.fuelCost)} />
|
<Cell label="Fuel" value={formatCredits(option.fuelCost)} />
|
||||||
|
{option.pilotId !== null &&
|
||||||
|
!state.pilots.find((p) => p.id === option.pilotId)?.isAvatar && (
|
||||||
|
<Cell label="Pilot share" value={formatCredits(option.pilotPay)} />
|
||||||
|
)}
|
||||||
<Cell label="Net profit" value={formatCredits(option.netProfit)} good />
|
<Cell label="Net profit" value={formatCredits(option.netProfit)} good />
|
||||||
<Cell
|
<Cell
|
||||||
label="ETA (game)"
|
label="ETA (game)"
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ function returningPlayer(acknowledged: boolean): PlayerState {
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 230,
|
loanRepayment: 230,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ const summaries: ResolutionSummary[] = [
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 32,
|
pilotXp: 32,
|
||||||
discoveredLocationId: "mars",
|
discoveredLocationId: "mars",
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
@ -51,6 +52,7 @@ const summaries: ResolutionSummary[] = [
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ describe("the deal", () => {
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ describe("loan on HQ", () => {
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 230,
|
loanRepayment: 230,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
import { content } from "@deadhead/content";
|
||||||
|
import type { Contract, Pilot, ResolutionSummary, Ship } from "@deadhead/schema";
|
||||||
|
import { pilotShare, realToGame, tripFuelCost } from "@deadhead/sim";
|
||||||
|
import { screen, within } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { useClock } from "../src/clock/clock";
|
||||||
|
import { formatCredits } from "../src/format";
|
||||||
|
import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render";
|
||||||
|
|
||||||
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
||||||
|
|
||||||
|
const ship = (id: string, name: string, pilotId: string): Ship => ({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
modelId: "courier",
|
||||||
|
status: "idle",
|
||||||
|
locationId: "earth",
|
||||||
|
dispatchId: null,
|
||||||
|
pilotId,
|
||||||
|
lastArrivalAt: 0,
|
||||||
|
...shipExtras("courier"),
|
||||||
|
});
|
||||||
|
const pilot = (id: string, name: string, shipId: string, isAvatar: boolean): Pilot => ({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
isAvatar,
|
||||||
|
isVeteran: !isAvatar,
|
||||||
|
xp: 0,
|
||||||
|
level: 0,
|
||||||
|
skills: {},
|
||||||
|
skillPointsAvailable: 0,
|
||||||
|
assignedShipId: shipId,
|
||||||
|
status: "idle",
|
||||||
|
locationId: "earth",
|
||||||
|
dutyHours: 0,
|
||||||
|
dutyLimit: 12,
|
||||||
|
resting: false,
|
||||||
|
restEndsAt: null,
|
||||||
|
});
|
||||||
|
const job: Contract = {
|
||||||
|
id: "c1",
|
||||||
|
originId: "earth",
|
||||||
|
destinationId: "moon",
|
||||||
|
cargoTypeId: "standard",
|
||||||
|
goodId: "consumer-goods",
|
||||||
|
shipper: "Pacific Orbital Logistics",
|
||||||
|
priority: false,
|
||||||
|
deliveryWindowGameHours: null,
|
||||||
|
tier: 0,
|
||||||
|
massTonnes: 10,
|
||||||
|
payout: 900,
|
||||||
|
requiredSkill: null,
|
||||||
|
requiredLongHaulLevel: 0,
|
||||||
|
offeredAt: NOW,
|
||||||
|
expiresAt: NOW + 30 * 60_000,
|
||||||
|
firstContact: false,
|
||||||
|
};
|
||||||
|
const summary: ResolutionSummary = {
|
||||||
|
dispatchId: "d1",
|
||||||
|
shipId: "s-vet",
|
||||||
|
pilotId: "p-vet",
|
||||||
|
originId: "earth",
|
||||||
|
destinationId: "moon",
|
||||||
|
tutorial: false,
|
||||||
|
deadhead: false,
|
||||||
|
fuelCost: 150,
|
||||||
|
goodId: "consumer-goods",
|
||||||
|
massTonnes: 10,
|
||||||
|
shipper: "Pacific Orbital Logistics",
|
||||||
|
credits: 600,
|
||||||
|
loanRepayment: 0,
|
||||||
|
pilotPay: 63,
|
||||||
|
pilotXp: 10,
|
||||||
|
discoveredLocationId: null,
|
||||||
|
scriptedId: null,
|
||||||
|
restEndsAt: null,
|
||||||
|
resolvedAt: NOW,
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = (summaries: ResolutionSummary[] = []) =>
|
||||||
|
samplePlayerState({
|
||||||
|
tutorial: null,
|
||||||
|
company: { id: "c", name: "D", credits: 100_000, loanBalance: 0 },
|
||||||
|
bays: [{ id: "b", locationId: "earth" }],
|
||||||
|
ships: [ship("s-me", "My Lark", "avatar"), ship("s-vet", "Rosa's Lark", "p-vet")],
|
||||||
|
pilots: [pilot("avatar", "Captain", "s-me", true), pilot("p-vet", "Rosa Kell", "s-vet", false)],
|
||||||
|
summaries,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("pilot share in the client (Step 10J.6)", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
||||||
|
vi.setSystemTime(NOW);
|
||||||
|
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
||||||
|
});
|
||||||
|
afterEach(() => vi.useRealTimers());
|
||||||
|
|
||||||
|
it("shows the hired pilot's share and the net profit after it, and no share for the avatar", async () => {
|
||||||
|
renderApp(
|
||||||
|
fakeApi({
|
||||||
|
getPlayerState: () => state(),
|
||||||
|
getContractBoard: () => ({ contracts: [job], periodEndsAt: NOW + 600_000 }),
|
||||||
|
}),
|
||||||
|
"/contracts?location=earth",
|
||||||
|
);
|
||||||
|
await userEvent.click(await screen.findByRole("button", { name: "Dispatch" }));
|
||||||
|
const dialog = screen.getByRole("dialog");
|
||||||
|
const fuel = tripFuelCost(
|
||||||
|
content.shipModel("courier")!,
|
||||||
|
content.location("earth")!,
|
||||||
|
content.location("moon")!,
|
||||||
|
realToGame(NOW, content.config),
|
||||||
|
);
|
||||||
|
const share = pilotShare(job.payout, fuel, content.config);
|
||||||
|
|
||||||
|
const hired = within(dialog).getByText("Rosa's Lark").closest("li")!;
|
||||||
|
expect(hired).toHaveTextContent(`Pilot share${formatCredits(share)}`);
|
||||||
|
expect(hired).toHaveTextContent(`Net profit${formatCredits(job.payout - fuel - share)}`);
|
||||||
|
|
||||||
|
const mine = within(dialog).getByText("My Lark").closest("li")!;
|
||||||
|
expect(mine).not.toHaveTextContent("Pilot share");
|
||||||
|
expect(mine).toHaveTextContent(`Net profit${formatCredits(job.payout - fuel)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists the pilot's share in a delivery summary", async () => {
|
||||||
|
renderApp(fakeApi({ getPlayerState: () => state([summary]) }), "/");
|
||||||
|
const dialog = await screen.findByRole("dialog", { name: "While you were away" });
|
||||||
|
expect(dialog).toHaveTextContent(`${formatCredits(63)} pilot share to Rosa Kell`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -40,6 +40,7 @@ const summary = (extra: Partial<ResolutionSummary>): ResolutionSummary => ({
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: null,
|
scriptedId: null,
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ describe("scripted delivery summary", () => {
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: "unmarked-crates",
|
scriptedId: "unmarked-crates",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ Living reference for how the code is organised. Update it after any database sch
|
||||||
| `LocationState` | `id`, `discovered` |
|
| `LocationState` | `id`, `discovered` |
|
||||||
| `Contract` | mirrors sim `Contract`: route, `cargoTypeId` (class), `goodId`, `shipper`, `priority`, `deliveryWindowGameHours` (Priority only, else null), `tier`, `massTonnes` (to 0.1 t under 10 t), `payout`, requirements, `offeredAt`/`expiresAt`, `firstContact`, optional `scriptedId` on a scripted tutorial contract (CR-007) |
|
| `Contract` | mirrors sim `Contract`: route, `cargoTypeId` (class), `goodId`, `shipper`, `priority`, `deliveryWindowGameHours` (Priority only, else null), `tier`, `massTonnes` (to 0.1 t under 10 t), `payout`, requirements, `offeredAt`/`expiresAt`, `firstContact`, optional `scriptedId` on a scripted tutorial contract (CR-007) |
|
||||||
| `Dispatch` | mirrors sim `DispatchRecord` + `status` (`in-flight` / `resolved`), `resolvedAt` |
|
| `Dispatch` | mirrors sim `DispatchRecord` + `status` (`in-flight` / `resolved`), `resolvedAt` |
|
||||||
| `ResolutionSummary` | `dispatchId`, `shipId`, `pilotId`, `originId`, `destinationId`, `tutorial`, `deadhead`, `fuelCost`, `goodId` / `massTonnes` / `shipper` (null for a deadhead, CR-007), `credits` (net), `loanRepayment`, `pilotXp`, `discoveredLocationId`, `scriptedId`, `restEndsAt` (set when the trip ended in mandatory rest), `resolvedAt` |
|
| `ResolutionSummary` | `dispatchId`, `shipId`, `pilotId`, `originId`, `destinationId`, `tutorial`, `deadhead`, `fuelCost`, `goodId` / `massTonnes` / `shipper` (null for a deadhead, CR-007), `credits` (net), `loanRepayment`, `pilotPay` (hired pilot's profit share, CR-010), `pilotXp`, `discoveredLocationId`, `scriptedId`, `restEndsAt` (set when the trip ended in mandatory rest), `resolvedAt` |
|
||||||
| `TutorialState` | `loanerLocationId` (`earth` / `moon`), `runInFlight`, `runsCompleted`, `dealAvailable` |
|
| `TutorialState` | `loanerLocationId` (`earth` / `moon`), `runInFlight`, `runsCompleted`, `dealAvailable` |
|
||||||
| `GoalState` | `id`, `status` (`incomplete` / `ready` / `claimed`) |
|
| `GoalState` | `id`, `status` (`incomplete` / `ready` / `claimed`) |
|
||||||
| `PlayerState` | `company`, `ships`, `pilots`, `bays`, `locations`, `tutorial` (null once over), `activeDispatches`, `summaries`, `goals` (empty during the tutorial) |
|
| `PlayerState` | `company`, `ships`, `pilots`, `bays`, `locations`, `tutorial` (null once over), `activeDispatches`, `summaries`, `goals` (empty during the tutorial) |
|
||||||
|
|
|
||||||
|
|
@ -245,5 +245,5 @@ Spec: `memory-bank/change-requests/CR-010-veteran-pilot-profit-share.md` (receiv
|
||||||
- [x] **Step 10J.3 — Sim: goal change.** Goal 4 is now `veteran-contract`, "Fly a contract with Rosa Kell" (title from `content.veteranPilot.name`), condition `veteran-contracts` ≥ 1; `GoalProgress.veteranContracts`. The server reports 0 until the veteran is stored (10J.4). Tests: `goals.test.ts` (avatar deliveries alone don't complete it; one veteran contract does) and the goal-id updates in server and web goal tests.
|
- [x] **Step 10J.3 — Sim: goal change.** Goal 4 is now `veteran-contract`, "Fly a contract with Rosa Kell" (title from `content.veteranPilot.name`), condition `veteran-contracts` ≥ 1; `GoalProgress.veteranContracts`. The server reports 0 until the veteran is stored (10J.4). Tests: `goals.test.ts` (avatar deliveries alone don't complete it; one veteran contract does) and the goal-id updates in server and web goal tests.
|
||||||
- [x] **Step 10J.4 — Server: veteran on the deal.** Migration `0007_cr010_veteran_pilot`: `pilots.is_veteran`. `acceptDeal` inserts the veteran in the same transaction: content name, XP at level 2's threshold, Heavy Cargo 1 bought (1 point spent, 3 unspent), at Earth, unassigned. API `Pilot.isVeteran`. Goal progress counts resolved contract deliveries (not deadheads or tutorial runs) by the veteran. Test: `veteran.test.ts` (state after the deal; no veteran on a refused or repeated accept; avatar rests, is swapped out, the veteran flies at once and is paid a profit share, goal 4 ready; avatar deliveries and a veteran deadhead don't count).
|
- [x] **Step 10J.4 — Server: veteran on the deal.** Migration `0007_cr010_veteran_pilot`: `pilots.is_veteran`. `acceptDeal` inserts the veteran in the same transaction: content name, XP at level 2's threshold, Heavy Cargo 1 bought (1 point spent, 3 unspent), at Earth, unassigned. API `Pilot.isVeteran`. Goal progress counts resolved contract deliveries (not deadheads or tutorial runs) by the veteran. Test: `veteran.test.ts` (state after the deal; no veteran on a refused or repeated accept; avatar rests, is swapped out, the veteran flies at once and is paid a profit share, goal 4 ready; avatar deliveries and a veteran deadhead don't count).
|
||||||
- [x] **Step 10J.5 — Client: deal modal and pilot card.** The deal modal says Halvorsen's pilot comes with the business and lists "Pilot: …and Rosa Kell. Halvorsen's pilot for twelve years." (from content). The veteran's Pilots card shows the bio line under the name. Test: `veteran-ui.test.tsx`.
|
- [x] **Step 10J.5 — Client: deal modal and pilot card.** The deal modal says Halvorsen's pilot comes with the business and lists "Pilot: …and Rosa Kell. Halvorsen's pilot for twelve years." (from content). The veteran's Pilots card shows the bio line under the name. Test: `veteran-ui.test.tsx`.
|
||||||
- [ ] Step 10J.6 — Client: pilot share in dispatch and summaries
|
- [x] **Step 10J.6 — Client: pilot share in dispatch and summaries.** The dispatch sheet shows "Pilot share" between Fuel and Net profit for hired pilots (sim `pilotShare`), none for the avatar; net profit is payout − fuel − share. API `ResolutionSummary.pilotPay` (from the stored resolution); the away summary shows "[share] pilot share to [pilot]" when above zero. Test: `pilot-share.test.tsx`.
|
||||||
- [ ] Step 10J.7 — Plan amendments and regression
|
- [ ] Step 10J.7 — Plan amendments and regression
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,8 @@ export const resolutionSummarySchema = z.object({
|
||||||
credits,
|
credits,
|
||||||
/** Credits the lender took from this payout (decision 0.5.32). */
|
/** Credits the lender took from this payout (decision 0.5.32). */
|
||||||
loanRepayment: credits.nonnegative(),
|
loanRepayment: credits.nonnegative(),
|
||||||
|
/** A hired pilot's profit share of this delivery; 0 for the avatar, tutorial runs and deadheads (CR-010). */
|
||||||
|
pilotPay: credits.nonnegative(),
|
||||||
pilotXp: z.number().int().nonnegative(),
|
pilotXp: z.number().int().nonnegative(),
|
||||||
/** Set when the delivery discovered its destination. */
|
/** Set when the delivery discovered its destination. */
|
||||||
discoveredLocationId: idSchema.nullable(),
|
discoveredLocationId: idSchema.nullable(),
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ const summary = {
|
||||||
massTonnes: 12,
|
massTonnes: 12,
|
||||||
shipper: "Pacific Orbital Logistics",
|
shipper: "Pacific Orbital Logistics",
|
||||||
loanRepayment: 0,
|
loanRepayment: 0,
|
||||||
|
pilotPay: 0,
|
||||||
pilotXp: 10,
|
pilotXp: 10,
|
||||||
discoveredLocationId: null,
|
discoveredLocationId: null,
|
||||||
scriptedId: "unmarked-crates",
|
scriptedId: "unmarked-crates",
|
||||||
|
|
@ -215,6 +216,12 @@ const cases: [string, z.ZodType, unknown, unknown][] = [
|
||||||
],
|
],
|
||||||
["dispatch", dispatchSchema, dispatch, { ...dispatch, status: "lost" }],
|
["dispatch", dispatchSchema, dispatch, { ...dispatch, status: "lost" }],
|
||||||
["resolution summary", resolutionSummarySchema, summary, { ...summary, pilotXp: -5 }],
|
["resolution summary", resolutionSummarySchema, summary, { ...summary, pilotXp: -5 }],
|
||||||
|
[
|
||||||
|
"summary with a pilot share",
|
||||||
|
resolutionSummarySchema,
|
||||||
|
{ ...summary, pilotPay: 63 },
|
||||||
|
{ ...summary, pilotPay: -1 },
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"summary with rest started",
|
"summary with rest started",
|
||||||
resolutionSummarySchema,
|
resolutionSummarySchema,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue