Files
Dune/components/moritani-retention.tsx
T

127 lines
4.5 KiB
TypeScript

'use client';
import { useId, useState } from 'react';
import type { Action, GameView } from '@/game/engine';
import { battleCardLabel } from '@/game/battle-cards';
import { Button } from './ui/button';
import { CardInspector, CardRules } from './card-inspector';
export function MoritaniRetention({
game,
act,
busy,
}: {
game: GameView;
act: (action: Action) => void;
busy: boolean;
}) {
const groupId = useId();
const [selected, setSelected] = useState<string | null>(null);
const decision = game.decision;
if (decision?.kind !== 'moritaniRetention' || decision.player !== game.me)
return null;
const nexus = decision.source === 'nexus';
const canKeep = !nexus || !!game.nexusMoritaniRetention?.canKeep;
const choose = (keep: string | null): Action => nexus
? { type: 'decision', event: decision.event, keep }
: { type: 'decision', keep };
const me = game.players.find((player) => player.id === game.me);
if (!me) return null;
const cards = (me.hand ?? []).filter((card) =>
decision.cards.includes(card.id),
);
const selectedCard = cards.find((card) => card.id === selected);
const owner = game.players.find((player) => player.id === decision.owner);
const location = game.combatLocations.find(place => place.id === decision.territory)?.name
?? decision.territory;
const disabled = busy || !!game.response || !!game.truthtrance;
return (
<section
className="min-w-0 space-y-4"
aria-label={nexus ? 'Postbattle Moritani Nexus choice' : 'Moritani allied card retention'}
>
{nexus ? (
<>
<p>
You lost the battle in {location}. Every
loser with an eligible played card gets this choice. If you hold the
Moritani Nexus card while unallied, spend it to retain one played
card a winner could keep; otherwise continue.
</p>
<p className="fine">
Used cards with mandatory discard cannot be retained. Spending the
Nexus card is public; your other hand cards remain private.
</p>
</>
) : (
<>
<p>
You lost the battle in {location}. Your
Moritani ally{owner ? `, ${owner.name},` : ''} lets you choose one
eligible card played in that battle to keep. Using this ability is
optional.
</p>
<p className="fine">
Declare one card, then the table may respond with Karama before you
retain it. If the power is canceled, all these cards are discarded.
Declining also discards them.
</p>
</>
)}
<fieldset
className="m-0 min-w-0 space-y-3 border-0 p-0"
disabled={disabled}
>
<legend className="mb-3 font-semibold">
Choose one card to retain
</legend>
{cards.map((card) => (
<article
key={card.id}
className={`treachery-card card-${card.kind} min-w-0`}
>
<label
htmlFor={`${groupId}-${card.id}`}
className="flex min-h-11 cursor-pointer items-center gap-3 break-words"
>
<input
id={`${groupId}-${card.id}`}
type="radio"
name={groupId}
value={card.id}
checked={selectedCard?.id === card.id}
onChange={() => setSelected(card.id)}
className="size-5 shrink-0 accent-[#d7b673]"
/>
<span className="min-w-0 font-semibold">{card.name}</span>
</label>
<p className="eyebrow">{battleCardLabel(card.kind)}</p>
<CardRules card={card} />
<CardInspector card={card} />
</article>
))}
</fieldset>
{!cards.length && (
<p className="fine">
No eligible card remains in your hand. Decline to continue.
</p>
)}
<Button
className="min-h-11 w-full whitespace-normal"
disabled={disabled || !selectedCard || !canKeep}
onClick={() => selectedCard && canKeep && act(choose(selectedCard.id))}
>
{nexus ? 'Spend Moritani Nexus · retain selected card' : 'Declare selected card'}
</Button>
<Button
variant="outline"
className="min-h-11 w-full whitespace-normal"
disabled={disabled}
onClick={() => act(choose(null))}
>
{nexus ? 'Continue without retention' : 'Decline retention'}
</Button>
</section>
);
}