// The replay's controls in the top bar (step, play, speed, whose view), the live seat switch, and the end of a game as // 无名杀's result dialog. import type { GameView } from '../../../src/engine/types'; import type { UseReplayResult, ReplaySpeed } from '../../../src/client/hooks/useReplay'; import { ID_SHORT, ID_KEY } from './Seat'; import { portrait } from './art'; export function ReplayBar({ r }: { r: UseReplayResult }) { return (
r.goTo(Number(e.target.value))} aria-label="进度" /> {r.step}/{r.total}
); } /** The replay's speed and whose view it shows (a seat's, or the spectator's). */ export function ReplayView({ r, names }: { r: UseReplayResult; names: string[] }) { return (
); } /** The live seat switch, when this viewer controls more than one seat. */ export function SeatSwitch({ seats, seat, view, onPick }: { seats: string[]; seat?: string; view: GameView; onPick: (s: string) => void }) { if (seats.length < 2) return null; return (
{seats.map((x) => ( ))}
); } const CAMP: Record = { 主公: '主公', 忠臣: '主公', 反贼: '反贼', 内奸: '内奸' }; const COUNT: Record = { 2: [0, 1], 3: [0, 1], 4: [1, 1], 5: [1, 2], 6: [1, 3], 7: [2, 3], 8: [2, 4] }; /** Who won, from what the view shows at the end: the lord alive means the lord's side; the lord dead, the rebels, unless * the one left alive is the renegade (every loyalist and rebel is dead and shown). */ export function inferWinner(view: GameView): string { const lord = view.players.find((p) => p.identity === '主公'); if (!lord) return ''; if (lord.alive) return '主公'; const alive = view.players.filter((p) => p.alive); const [zhong, fan] = COUNT[view.players.length] ?? [1, 2]; const dead = (id: string) => view.players.filter((p) => !p.alive && p.identity === id).length; return alive.length === 1 && dead('忠臣') === zhong && dead('反贼') === fan ? '内奸' : '反贼'; } export function Result({ view, camp, identities, me, labels }: { view: GameView; camp: string; identities?: Record; me: number; labels?: string[] }) { const idOf = (i: number) => identities?.[String(i)] || view.players[i]?.identity || ''; const mine = me >= 0 ? CAMP[idOf(me)] : ''; const title = !camp ? '游戏结束' : mine ? (mine === camp ? '游戏胜利' : '游戏失败') : `${camp === '主公' ? '主公、忠臣' : camp}获胜`; return (
{title}
{view.players.map((p, i) => { const id = idOf(i); const img = portrait(p.character); return ( ); })}
{p.character} {labels?.[i] ?? (p.name !== p.character ? p.name : '')} {id ? ID_SHORT[id] : ''} {id || '未知'} {p.alive ? '存活' : '阵亡'} {CAMP[id] === camp ? '胜' : id ? '负' : ''}
); }