Files
Dune/components/nexus-emperor-secret-ally.tsx

132 lines
5.0 KiB
TypeScript

'use client';
import { useState } from 'react';
import type { Action, GameView } from '@/game/engine';
import { nexusEmperorPurchaseAction, nexusEmperorRevivalAction } from '@/game/nexus-emperor-secret-ally-options';
import { Button } from '@/components/ui/button';
export function NexusEmperorSecretAlly({
game,
act,
busy,
}: {
game: GameView;
act: (action: Action) => void;
busy: boolean;
}) {
const [selectedElite, setSelectedElite] = useState<number | null>(null);
const offer = game.nexusEmperorSecretAlly;
const owner = game.players.find((player) => player.id === game.me);
const emperorSeated = game.players.some((player) => player.faction === 'emperor');
const purchase =
offer?.purchase &&
game.nexusCards?.card === 'emperor' &&
!!owner &&
!emperorSeated &&
game.status === 'playing' &&
game.phase === 3 &&
game.decision?.kind === 'auctionPayment' &&
game.decision.player === game.me
? offer.purchase
: null;
if (purchase) {
const action = nexusEmperorPurchaseAction(game);
const blocked = busy || !action;
const reason = purchase.blocked ??
(!action
? (owner?.spice ?? 0) < purchase.price
? `You need ${purchase.price} spice in your own supply for this bid.`
: 'Finish the current interaction before spending the Emperor Nexus card.'
: null);
return (
<section className="min-w-0 space-y-3" aria-label="Emperor Secret Ally purchase">
<h3 className="font-serif text-xl">Emperor Secret Ally</h3>
<p className="text-base leading-7">
Your final bid is {purchase.price} spice. You must have all {purchase.price} spice
in your own supply without ally aid. Spend the Emperor Nexus card to keep that
spice instead of paying the bank; you receive the auction card normally.
</p>
{reason && <p className="notice">{reason}</p>}
<Button
className="min-h-11 whitespace-normal"
disabled={blocked}
onClick={() => {
if (!blocked && action) act(action);
}}
>
Spend Emperor Nexus card · retain {purchase.price} spice
</Button>
</section>
);
}
const eligible =
offer?.revival &&
game.nexusCards?.card === 'emperor' &&
!!owner &&
!owner.ally &&
!emperorSeated &&
game.status === 'playing' &&
game.phase === 4 &&
!game.response &&
!game.decision &&
!game.truthtrance &&
!game.phaseOpening &&
!game.automaticContinuationPending &&
game.nexusCards.waiting.length === 0 &&
!game.nexusTraitors?.pending;
if (!eligible) return null;
const options = offer.revival.eliteOptions;
const amount = offer.revival.amount ?? 3;
const elite = options.length === 1
? options[0]
: selectedElite !== null && options.includes(selectedElite)
? selectedElite
: null;
const action = elite === null ? null : nexusEmperorRevivalAction(game, elite);
const blocked = busy || !action;
return (
<section className="min-w-0 space-y-3" aria-label="Emperor Secret Ally revival">
<h3 className="font-serif text-xl">Emperor Secret Ally</h3>
<p className="text-base leading-7">
Spend the Emperor Nexus card to request {amount} additional {amount === 1 ? 'force' : 'forces'} for free
through the normal revival continuation, beyond ordinary force/free allowances.
Native typed caps and prevention still apply; Recruits does not double this grant.
</p>
<p className="notice">
Provisional short-group policy: return exactly 3 when eligible; otherwise return
all 1 or 2 eligible physical forces. This is not publisher clarification.
No eligible forces means the card cannot be spent. This grant revives no leader.
</p>
{offer.revival.blocked && <p className="notice">{offer.revival.blocked}</p>}
{offer.revival.policy && <p className="notice">{offer.revival.policy}</p>}
{options.length > 1 && (
<label className="block space-y-2">
<span className="font-semibold">Elite forces to revive</span>
<select
aria-label="Elite forces to revive"
className="min-h-11 w-full rounded border border-[#65644b] bg-[#171b17] px-3 py-2"
value={elite ?? ''}
disabled={busy || !!offer.revival.blocked}
onChange={(event) => setSelectedElite(event.currentTarget.value === '' ? null : Number(event.currentTarget.value))}
>
<option value="">Choose an elite count</option>
{options.map((count) => (
<option key={count} value={count}>{count}</option>
))}
</select>
</label>
)}
<Button
className="min-h-11 whitespace-normal"
disabled={blocked}
onClick={() => {
if (!blocked && action) act(action);
}}
>
{amount > 0 ? `Revive ${amount} additional ${amount === 1 ? 'force' : 'forces'}` : 'No eligible forces to revive'}{elite === null ? '' : ` · ${elite} elite`}
</Button>
</section>
);
}