feat: v0.12.0 — contracts → packages: load/fly/abandon gameplay (ATR 72-600 capacity 6, manual flight to any airport, time-scaled abandon penalty 15–60%), one-time DB migration preserving wallet/clock; new endpoints /api/packages/:id/load|abandon + /api/planes/:planeId/fly
This commit is contained in:
+15
-7
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState, type ChangeEvent } from 'react';
|
||||
import type { ContractState, PlaneState, StateSnapshot } from '@/lib/game/state';
|
||||
import type { PlaneState, StateSnapshot } from '@/lib/game/state';
|
||||
import { PLANE_TYPES } from '@/lib/game/catalog';
|
||||
import { formatCash, formatFuel, formatSimClock } from '@/lib/format';
|
||||
import { TIME_SCALES, scaleToSlider, sliderToScale } from '@/lib/timeScale';
|
||||
@@ -19,14 +19,21 @@ interface LeftPaneProps {
|
||||
const SCALE_SLIDER_STEP = 1 / (TIME_SCALES.length - 1); // one doubling per keyboard-arrow step
|
||||
const SCALE_COMMIT_DEBOUNCE_MS = 250;
|
||||
|
||||
function planeLocation(p: PlaneState, contracts: ContractState[]): string {
|
||||
if (p.status === 'flying' && p.contractId) {
|
||||
const c = contracts.find((c) => c.id === p.contractId);
|
||||
if (c) return `${p.locationName} → ${c.destName}`;
|
||||
}
|
||||
// Flying planes show their current leg from the snapshot's dest fields (v0.12.0).
|
||||
function planeLocation(p: PlaneState): string {
|
||||
if (p.status === 'flying' && p.destName) return `${p.locationName} → ${p.destName}`;
|
||||
return p.locationName;
|
||||
}
|
||||
|
||||
// Cargo loadout "n/capacity pkgs" — expired packages still occupy their slot.
|
||||
function loadoutFor(state: StateSnapshot, p: PlaneState): string {
|
||||
const used = state.packages.filter(
|
||||
(pkg) => pkg.planeId === p.id && (pkg.status === 'loaded' || pkg.status === 'expired'),
|
||||
).length;
|
||||
const capacity = state.catalog.find((e) => e.model === p.model)?.capacity ?? PLANE_TYPES[p.model]?.capacity ?? 0;
|
||||
return `${used}/${capacity} pkgs`;
|
||||
}
|
||||
|
||||
export default function LeftPane({ state, loading, error, onRefresh }: LeftPaneProps) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
@@ -156,7 +163,8 @@ export default function LeftPane({ state, loading, error, onRefresh }: LeftPaneP
|
||||
</div>
|
||||
<div className="plane-sub">
|
||||
<span data-testid={`plane-fuel-${p.id}`}>{formatFuel(p.fuel, p.maxFuel)}</span>
|
||||
<span className="plane-loc">{planeLocation(p, state.contracts)}</span>
|
||||
<span data-testid={`plane-loadout-${p.id}`}>{loadoutFor(state, p)}</span>
|
||||
<span className="plane-loc">{planeLocation(p)}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user