Connect bounded Diplomat retreat and record live deployment
This commit is contained in:
1 parent
4801ce003b
commit
c9a3625ff2
18 files changed
+601
-28
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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} />
|
||||
|
||||
Reference in new issue
Block a user