import type { GameView } from '@/game/engine'; import { faction } from '@/game/catalog'; import { leaderStrengthLabel } from '@/game/cards'; import { isStoneBurner } from '@/game/battle-cards'; import { stoneBurnerComparison } from '@/game/stone-burner'; import { RevealedPlanPieces } from './revealed-plan-pieces'; import { KwisatzInspector } from './kwisatz-inspector'; export function RevealedBattle({ game }: { game: GameView }) { const battle = game.battle; if (!battle?.revealed) return null; const attackerPlan = battle.plans[battle.attacker]; const defenderPlan = battle.plans[battle.defender]; const comparisons = battle.cards.some(isStoneBurner) && battle.ownForces && attackerPlan && defenderPlan ? (battle.stoneBurnerContext?.opponentPools ?? []).map((opponent) => stoneBurnerComparison( game.me === battle.attacker ? battle.ownForces! : opponent, attackerPlan.dial, attackerPlan.support, game.me === battle.defender ? battle.ownForces! : opponent, defenderPlan.dial, defenderPlan.support, ), ) : []; const name = (id: string | null) => id ? (game.allLeaders.find((l) => l.id === id)?.name ?? battle.cards.find((c) => c.id === id)?.name ?? id) : 'None'; return (

Revealed battle plans

Return to battle decisions

Both plans are public. Inspect any played component; use the decision controls to resolve the battle. Component guide · Battle rules

{[battle.attacker, battle.defender].map((id) => { const plan = battle.plans[id]; if (!plan) return null; const leader = game.allLeaders.find((l) => l.id === plan.leader); const diplomat = battle.diplomatDefense?.player === id ? battle.diplomatDefense : null; const undialed = [ ...new Set( comparisons.flatMap( (comparison) => comparison[id === battle.attacker ? 'attacker' : 'defender'], ), ), ].sort((a, b) => a - b); return (

{game.players.find((p) => p.id === id)?.name} · {id === battle.attacker ? 'Attacker' : 'Defender'}

Dial {plan.dial} {game.advanced ? ` · ${plan.support} spice support` : ''} {!!plan.allyPayment && ` (${plan.allyPayment} from CHOAM)`} {!!plan.bankerSpice && ( Spice Banker: {plan.bankerSpice} spice committed )} {battle.native === id && !battle.cards.some(isStoneBurner) && ( Native Homeworld bonus: +{battle.nativeBattleStrength} to the battle score, separately from the dial )} {plan.kwisatz && ( Kwisatz Haderach · +2 if leader survives · protected from traitors )} {undialed.length > 0 && ( Stone Burner undialed tokens: {undialed.join(' or ')} {undialed.length > 1 ? ' (possible counts before casualty choice)' : ''} )} {battle.stoneBurner[id] && ( Stone Burner:{' '} {battle.stoneBurner[id] === 'kill' ? 'both leaders die' : 'surviving leader strength ignored'}{' '} · compare undialed force tokens )} {battle.poisonTooth[id] !== undefined && ( Poison Tooth: {battle.poisonTooth[id] ? 'activated' : 'unused'} )} {diplomat?.stage === 'copied' && diplomat.card && ( Diplomat: {name(diplomat.card)} copies {name(diplomat.source)} · discard after battle )} {diplomat?.stage === 'declined' && ( Diplomat: defense copy declined )} card.id === plan.leader)} weapon={battle.cards.find(card => card.id === plan.weapon)} defense={battle.cards.find(card => card.id === plan.defense)} lateDefense={battle.cards.find(card => card.id === battle.lateDefense[id])} /> {plan.kwisatz && }
); })}
); }