'use client'; import { useId, useState } from 'react'; import { splitLocation, territory } from '@/game/board'; import type { Action, GameView } from '@/game/engine'; import { defaultHarassWithdrawAllocation, harassWithdrawCommitments, quoteHarassWithdraw, type HarassWithdrawContext, type HarassWithdrawForces, type HarassWithdrawSelection, } from '@/game/harass-withdraw'; import { Button } from './ui/button'; import { Input } from './ui/input'; function compactSelection(selection: HarassWithdrawSelection) { return Object.fromEntries( Object.entries(selection) .filter(([, group]) => group.normal || group.elite) .map(([key, group]) => [key, { ...group }]), ); } export function harassWithdrawChoiceState( context: HarassWithdrawContext, dial: number, support: number, selection: HarassWithdrawSelection, ) { try { const quote = quoteHarassWithdraw( context, dial, support, compactSelection(selection), ); return { blocked: null, quote }; } catch (error) { return { blocked: error instanceof Error ? error.message : 'Choose the exact forces that return.', quote: null, }; } } function locationLabel(g: GameView, key: string) { if (key.startsWith('homeworld:')) return g.combatLocations?.find(place => place.id === key)?.name ?? key; const location = splitLocation(key); return `${territory(location.territory).name} · sector ${location.sector}`; } export function HarassWithdrawChoice({ game: g, act, busy, }: { game: GameView; act: (action: Action) => void; busy: boolean; }) { const id = useId(); const decision = g.decision; const offer = g.battle?.harassAllocation; const current = decision?.kind === 'harassWithdraw' && decision.player === g.me && offer?.event === decision.event; const context = offer?.context; const commitments = context ? harassWithdrawCommitments(context, offer.dial, offer.support) : []; const initial = context ? (offer?.selection ?? defaultHarassWithdrawAllocation( context, offer.dial, offer.support, commitments[0], )) : {}; const [selection, setSelection] = useState(initial); if (!current || !offer || !context) return null; const state = harassWithdrawChoiceState( context, offer.dial, offer.support, selection, ); const selectedCommitment = state.quote?.remaining; const commitmentValue = selectedCommitment ? `${selectedCommitment.normal}:${selectedCommitment.elite}` : ''; const update = ( key: string, kind: keyof HarassWithdrawForces, value: number, ) => setSelection((previous) => ({ ...previous, [key]: { normal: previous[key]?.normal ?? 0, elite: previous[key]?.elite ?? 0, [kind]: value, }, })); return (

Choose Harass & Withdraw forces

Your Battle Plan is revealed. Choose the exact undialed counters that return to reserves before the Traitor decision.

{g.homeworlds && (

These counters return to native Homeworld reserves. Advanced Emperor ordinary counters go to Kaitain and Sardaukar to Salusa; Basic keeps both types on its original primary World. Original commitment/support does not change when that population rises.

)} {Object.entries(context.locations) .sort(([a], [b]) => a.localeCompare(b)) .map(([key, available]) => (
{locationLabel(g, key)} {(['normal', 'elite'] as const).map((kind) => ( ))}
))} {state.quote ? (

Return {state.quote.returned.normal} ordinary and{' '} {state.quote.returned.elite} elite forces.

) : ( {state.blocked} )}
); }