'use client'; import { useState } from 'react'; import type { Action, GameView } from '@/game/engine'; import { specialForceName } from '@/game/combat'; import { homeworldName, homeworldShipmentChoice, richeseHomeworldMarkerChoice, type HomeworldShipmentSources, } from '@/game/homeworld-shipment-options'; import { Button } from './ui/button'; import { Input } from './ui/input'; /** The concealed marker does not withdraw a reserve counter until revealed. */ export function HomeworldNoFieldShipment({ game, act, busy }: { game: GameView; act: (action: Action) => void; busy: boolean; }) { const me = game.players.find(player => player.id === game.me); const info = game.richeseNoField; const [selectedDestination, setSelectedDestination] = useState(''); const [selectedToken, setSelectedToken] = useState(''); const [allyPayment, setAllyPayment] = useState(0); const [forces, setForces] = useState({ normal: 0, elite: 0 }); if (!me || me.faction !== 'richese' || info?.owner !== me.id || !info.private) return null; const deployed = info.private.deployed; const source = deployed && 'homeworld' in deployed.location ? deployed.location.homeworld : undefined; const targets = (game.homeworlds?.worlds ?? []).filter(world => (world.native !== me.id || !!source && game.advanced) && world.native !== me.ally && world.id !== source); const destination = targets.find(world => world.id === selectedDestination) ?? targets.find(world => world.native !== me.id) ?? targets[0]; const tokens = info.private.tokens.filter(token => source ? token.id === deployed?.tokenId : token.id !== info.private!.lastShipped); const token = tokens.find(candidate => candidate.id === selectedToken) ?? tokens[0]; const pool = source ? game.homeworlds?.worlds?.find(world => world.id === source)?.forces[me.id] : { normal: me.reserves - (me.elites?.reserves ?? 0), elite: me.elites?.reserves ?? 0 }; const quote = richeseHomeworldMarkerChoice(game, destination?.id ?? '', token?.id ?? '', allyPayment, source, forces); return
Ship a concealed No-Field

Provisional Homeworld rule: ship one concealed marker {source ? `from ${homeworldName(game, source)}` : 'from your supply'} to another non-allied Homeworld. Its value stays private and its represented forces remain in your reserves until the marker is revealed. On your own Homeworld, the represented forces are already in those reserves and are not added again. This uses your normal shipment. Physical forces from the same source may accompany it.

{pool &&
Accompanying forces from {source ? homeworldName(game, source) : 'your reserves'} {(['normal', 'elite'] as const).filter(kind => kind === 'normal' || pool.elite > 0).map(kind => )}
} {me.ally && } {quote.amount} total (including one marker) · {quote.cost === 1 ? 'one' : quote.cost} spice total · you pay {quote.ownPayment}, ally pays {quote.allyPayment}. {quote.blocked && ` ${quote.blocked}`}
; } export function HomeworldShipment({ game: g, act, busy, }: { game: GameView; act: (action: Action) => void; busy: boolean; }) { const worlds = g.homeworlds?.worlds ?? []; const me = g.players.find((player) => player.id === g.me)!; const available = worlds.filter((world) => { const force = world.forces[g.me]; return force && force.normal + force.elite > 0; }); const targets = worlds.filter( (world) => (g.advanced || world.native !== g.me || me.faction === 'guild') && !(!g.advanced && g.guildTransportCanceled && me.faction === 'guild' && world.native === me.id) && world.native !== me.ally, ); const [origin, setOrigin] = useState(available[0]?.id ?? ''); const [destination, setDestination] = useState( (targets.find((world) => world.id !== available[0]?.id && world.native !== g.me) ?? targets.find((world) => world.id !== available[0]?.id))?.id ?? '', ); const [sources, setSources] = useState({}); const [allyPayment, setAllyPayment] = useState(0); const [nexusEvent, setNexusEvent] = useState(''); const useNexus = g.karamaShipping?.player !== g.me && !!g.nexusGuildSecretAlly && nexusEvent === g.nexusGuildSecretAlly.event; const option = g.homeworldShipment; if (!option) return null; const imperial = g.advanced && me.faction === 'emperor'; const selectedOrigins = available.filter((world) => origin === 'imperial-pair' ? world.native === me.id : world.id === origin, ); const selected = Object.fromEntries( selectedOrigins.flatMap((world) => { const choice = sources[world.id]; return choice && choice.normal + choice.elite !== 0 ? [[world.id, choice]] : []; }), ); const quote = homeworldShipmentChoice( g, destination, selected, allyPayment, useNexus ? nexusEvent : undefined, ); const special = specialForceName(me.faction); return (
Ship to another Homeworld

Use your shipment to send forces from one Homeworld to another. Native reserves and foreign garrisons have separate source pools.{' '} {g.advanced ? 'You may return a foreign garrison to your own Homeworld.' : me.faction === 'guild' ? g.guildTransportCanceled ? 'The native Guild return permission is canceled: Junction cannot be a destination, even with a purchased price.' : 'Guild may also return a foreign garrison to Junction.' : 'Your own Homeworld cannot be a destination.'}{' '} Allied Homeworlds cannot be destinations.

{useNexus ? 'Selected Guild Nexus price: one spice per two forces, rounded up.' : g.karamaShipping?.player === g.me ? 'Purchased Karama price: one spice per two forces, rounded up, paid entirely to the bank even if the native Guild rate is canceled.' : me.faction === 'guild' ? g.guildRateCanceled ? 'The native Guild rate is canceled: pay one spice per force.' : 'Guild pays one spice per two forces, rounded up.' : 'Pay one spice per force.'}{' '} This uses your shipment; your normal movement remains available.

{g.nexusGuildSecretAlly && (

Spend the Nexus for its independent price of one spice per two forces, rounded up; it does not stack with a purchased Karama price. Existing source and destination restrictions still apply.

{g.nexusGuildSecretAlly.blocked && (

{g.nexusGuildSecretAlly.blocked}

)}
)} {option.blocked && ( {option.blocked} )}
{selectedOrigins.map((world) => { const pool = world.forces[me.id]; const chosen = sources[world.id] ?? { normal: 0, elite: 0 }; return (
{homeworldName(g, world.id)} ·{' '} {world.native === me.id ? 'native reserves' : 'foreign garrison'}

Available: {pool.normal} normal {pool.elite > 0 ? ` + ${pool.elite} ${special}` : ''}.

{(['normal', 'elite'] as const) .filter((kind) => kind === 'normal' || pool.elite > 0) .map((kind) => ( ))}
); })} {me.ally && ( )}

Your available spice: {me.spice ?? 0}. Allied pledge:{' '} {g.aid.available}.

{quote.amount} forces · {quote.cost} spice total {quote.amount > 0 ? ` · you pay ${quote.ownPayment}, ally pays ${quote.allyPayment}` : ''} .{quote.blocked && ` ${quote.blocked}`}
); }