61 lines
3.1 KiB
TypeScript
61 lines
3.1 KiB
TypeScript
'use client';
|
|
|
|
import { leaders } from '@/game/cards';
|
|
import type { GameView } from '@/game/engine';
|
|
import { Button } from './ui/button';
|
|
|
|
/** The parent owns timing and target availability; this editor only uses
|
|
* publicly listed native leader identities and never tests private plans. */
|
|
export function TruthFutureBattleLeader({ game, target, leader, available, busy, onLeaderChange, onAsk }: {
|
|
game: GameView;
|
|
target: string;
|
|
leader: string;
|
|
available: boolean;
|
|
busy: boolean;
|
|
onLeaderChange: (leader: string) => void;
|
|
onAsk: (target: string, leader: string) => void;
|
|
}) {
|
|
const respondent = target !== game.me ? game.players.find(player => player.id === target) : undefined;
|
|
const nativeIds = new Set(respondent ? leaders(respondent.faction).map(item => item.id) : []);
|
|
const nativeLeaders = game.allLeaders.filter(item => nativeIds.has(item.id) && item.faction === respondent?.faction);
|
|
const uniqueLeaders = nativeLeaders.filter((item, index) => nativeLeaders.findIndex(other => other.id === item.id) === index);
|
|
const selected = uniqueLeaders.find(item => item.id === leader);
|
|
const disabled = busy || !available || !respondent;
|
|
const blocked = disabled || !selected;
|
|
|
|
return <>
|
|
{!respondent && <p className="notice">Choose another seated player for this question.</p>}
|
|
{respondent && <label>
|
|
Leader
|
|
<select data-testid="truth-future-battle-leader-select" value={selected?.id ?? ''} disabled={disabled}
|
|
onChange={event => {
|
|
const next = event.target.value;
|
|
if (!disabled && uniqueLeaders.some(item => item.id === next)) onLeaderChange(next);
|
|
}}>
|
|
<option value="">Choose a leader</option>
|
|
{uniqueLeaders.map(item => <option key={item.id} value={item.id}>{item.name}</option>)}
|
|
</select>
|
|
</label>}
|
|
<blockquote data-testid="truth-future-battle-leader-preview">
|
|
If you fight a battle later this turn, will you use {selected?.name ?? '[leader]'} in your first such battle?
|
|
</blockquote>
|
|
<p className="notice">If you do not fight a later battle this turn, there is no opportunity to use that leader.</p>
|
|
<Button className="game-action" data-testid="truth-future-battle-leader-ask" disabled={blocked}
|
|
onClick={() => { if (!blocked) onAsk(target, leader); }}>Ask publicly</Button>
|
|
</>;
|
|
}
|
|
|
|
/** Only the answering seat receives this receipt in its projection. */
|
|
export function FutureBattleLeaderPromises({ game }: {game: GameView}) {
|
|
const promises = game.futureBattleLeaderPromises.filter(row => row.turn === game.turn &&
|
|
(row.status === 'waiting' || row.status === 'bound'));
|
|
if (!promises.length) return null;
|
|
return <section className="notice" aria-label="Your conditional battle answer">
|
|
{promises.map((row, index) => <p key={`${row.turn}-${row.player}-${index}`}>
|
|
Your Truthtrance answer commits you to {row.answer ? 'use' : 'not use'}{' '}
|
|
{game.allLeaders.find(leader => leader.id === row.leader)?.name ?? row.leader} in your first later battle this turn.
|
|
{row.status === 'waiting' ? ' If you fight no battle, there is no opportunity.' : ' This battle is the opportunity.'}
|
|
</p>)}
|
|
</section>;
|
|
}
|