Connect bounded Diplomat retreat and record live deployment

This commit is contained in:
Quintine committed 2026-09-28 09:18:36 +10:00
1 parent 4801ce003b
commit c9a3625ff2
18 files changed
+601 -28

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
'use client';
import { useId, useState } from 'react';
import { splitLocation, territory } from '@/game/board';
import type { Action, GameView } from '@/game/engine';
import { Button } from './ui/button';
export function DiplomatRetreat({
game,
act,
busy,
}: {
game: GameView;
act: (action: Action) => void;
busy: boolean;
}) {
const id = useId();
const [destination, setDestination] = useState<string | null>(null);
const [choiceIndex, setChoiceIndex] = useState(0);
const decision = game.decision?.kind === 'diplomatRetreat' ? game.decision : null;
if (!decision || decision.player !== game.me) return null;
const selectedDestination = decision.destinations.find((offer) => offer.location === destination)
?? decision.destinations[0];
const choice = selectedDestination?.choices[choiceIndex] ?? selectedDestination?.choices[0];
const selectedLocation = selectedDestination ? splitLocation(selectedDestination.location) : null;
return (
<section className="flex flex-col gap-3" aria-label="Diplomat retreat choice">
<p>
Your skilled Diplomat lost this battle. Move up to their strength in
undialed physical forces to one empty adjacent non-stronghold territory,
or leave the forces where they are.
</p>
{selectedDestination && choice && selectedLocation ? (
<>
<label htmlFor={`${id}-destination`}>Retreat destination</label>
<select
id={`${id}-destination`}
value={selectedDestination.location}
disabled={busy}
onChange={(event) => {
setDestination(event.target.value);
setChoiceIndex(0);
}}
>
{decision.destinations.map((offer) => {
const location = splitLocation(offer.location);
return (
<option key={offer.location} value={offer.location}>
{territory(location.territory).name} · sector {location.sector}
</option>
);
})}
</select>
<label htmlFor={`${id}-forces`}>Physical forces to retreat</label>
<select
id={`${id}-forces`}
value={selectedDestination.choices.indexOf(choice)}
disabled={busy}
onChange={(event) => setChoiceIndex(Number(event.target.value))}
>
{selectedDestination.choices.map((amount, index) => (
<option key={`${amount.normal}:${amount.elite}`} value={index}>
{amount.normal} ordinary · {amount.elite} elite
</option>
))}
</select>
<Button
className="game-action min-h-11 whitespace-normal"
disabled={busy}
onClick={() => !busy && act({
type: 'decision', event: decision.event,
destination: selectedDestination.location, normal: choice.normal, elite: choice.elite,
})}
>
Retreat to {territory(selectedLocation.territory).name} · sector {selectedLocation.sector}
</Button>
</>
) : (
<p className="muted">No legal retreat destination and force combination is available.</p>
)}
<Button
variant="outline"
className="game-action min-h-11 whitespace-normal"
disabled={busy}
onClick={() => !busy && act({
type: 'decision', event: decision.event,
destination: null, normal: 0, elite: 0,
})}
>
Decline Diplomat retreat
</Button>
</section>
);
}
+5
View File
@@ -180,6 +180,7 @@ import { canUsePlanetologistBattleSpecial } from '../game/leader-skill-combat';
import type { Card } from '../game/cards';
import { RevealedBattle } from './revealed-battle';
import { DiplomatDefense } from './diplomat-defense';
import { DiplomatRetreat } from './diplomat-retreat';
import { EliteCount } from './elite-count';
import { HelpTip } from './help-tip';
import { PHASE_HELP, phaseRuleId } from '@/game/reference';
@@ -2272,6 +2273,8 @@ export function GameTable({
? 'Save your battle casualties'
: g.decision.kind === 'harassWithdraw'
? 'Choose undialed forces to return'
: g.decision.kind === 'diplomatRetreat'
? 'Retreat undialed forces'
: g.decision.kind === 'diplomatDefense'
? 'Copy an opposing defense'
: g.decision.kind === 'homeworldRevivalDeployment'
@@ -2702,6 +2705,8 @@ export function GameTable({
<>
<HarassWithdrawChoice key={g.decision.event} game={g} act={act} busy={busy} />
</>
) : g.decision.kind === 'diplomatRetreat' ? (
<DiplomatRetreat key={g.decision.event} game={g} act={act} busy={busy} />
) : g.decision.kind === 'diplomatDefense' ? (
<>
<DiplomatDefense game={g} act={act} busy={busy} />