'use client'; import { useId, useState } from 'react'; import type { Action, GameView } from '@/game/engine'; import { location, territory } from '@/game/board'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { AmbassadorNoField } from './ambassador-no-field'; const bounded = (value: number, min: number, max: number) => Math.max(min, Math.min(max, Number.isSafeInteger(value) ? value : min)); /** Mounted with the Ambassador event key so an earlier shipment draft cannot carry over. */ export function AmbassadorShipment({ game, act, busy, }: { game: GameView; act: (action: Action) => void; busy: boolean; }) { const id = useId(); const [destinationKey, setDestinationKey] = useState(''); const [count, setCount] = useState(null); const [eliteCount, setEliteCount] = useState(0); const entry = game.ambassadorEntry; if ( entry?.stage !== 'ship' || game.decision?.kind !== 'ecazAmbassador' || game.decision.player !== game.me ) return null; if (entry.noField?.stage === 'supply' || entry.noField?.stage === 'consent') return ; if (entry.beneficiary !== game.me || !entry.shipment) return null; const me = game.players.find((p) => p.id === game.me)!; const shipment = entry.shipment; const destinations = shipment.destinations.map((d) => ({ territory: d.territory, sector: d.sector, blocked: d.blocked, })); for (const option of entry.noField?.options ?? []) { if (!option.territory || option.sector === undefined) continue; const existing = destinations.find((d) => d.territory === option.territory && d.sector === option.sector, ); if (existing) { if (!option.blocked) existing.blocked = null; } else destinations.push({ territory: option.territory, sector: option.sector, blocked: option.blocked, }); } const destination = destinations.find( (d) => location(d.territory, d.sector) === destinationKey, ) ?? destinations.find((d) => !d.blocked) ?? destinations[0]; const physicalDestination = shipment.destinations.find((d) => d.territory === destination?.territory && d.sector === destination?.sector, ); const maximum = Math.min( shipment.maximum, me.reserves, physicalDestination?.maximum ?? 0, ); const amount = maximum ? bounded(count ?? maximum, 1, maximum) : 0; const minimumElite = Math.max( 0, amount - (me.reserves - shipment.eliteReserves), ); const maximumElite = Math.min(amount, shipment.eliteReserves); const elite = bounded(eliteCount, minimumElite, maximumElite); const reason = !amount ? 'You have no reserve forces available for this shipment.' : !physicalDestination ? 'No shipment destination is currently available.' : physicalDestination.blocked; const territories = [ ...new Set(destinations.map((d) => d.territory)), ]; return (

Send up to four of your reserve forces for free, or send no forces. This does not spend your ordinary shipment or movement. The Ambassador has already been triggered.

{shipment.eliteReserves > 0 && ( )} {physicalDestination?.advisors && (

These forces join your advisors.

)}

Selected: {amount} physical forces, including {elite} elites. Cost: 0 spice.

{reason && (

{reason}

)}
); }