Files
Dune/components/private-battle-plan.tsx
T
Quintine 1380271bb6 Connect Spice Banker sealed battle spending and saved settlement
Prototype one through three own spice as a separate sealed battle commitment. Reserve funds alongside actual force support, add strength only for the surviving skilled leader, and settle once through existing versioned writes. Apply the documented card/core-rule interpretation: payment survives leader death or loss, except for the sole successful traitor caller. Keep printed disc strength and bounty unchanged. Normal income awaits its deferred-custody ruling; combined modules remain gated.

Validation: 54 focused tests; typecheck, lint, 4490 offline tests, production build and 43 HTTP tests passed. Independent rules, privacy, persistence and UI review completed. SQLite tests cover reserved-spice diversion, concurrent settlement, traitor waiver and legacy plans. Browser exercised a three-spice commitment, pending refresh, actual +3 strength and payment, and restored private cards and the next decision. Two genuine four-profile Basic/Advanced samples completed without rejected candidates but did not use Banker spending.

Following another reported outage, the server remained healthy. Preserved all 461 opening rooms and original seat/recovery rows (495 rooms after QA/HTTP). SQLite integrity and foreign keys passed. A fresh online backup captured all 495 rooms, and browser refresh restored the QA seat. No restart or reset was needed.

Verified source tree: 208bf9324ab994b5e59ed50d78998b4dbf8810b3b6799b1a577ef3b84479335f
2026-09-13 20:45:42 +10:00

87 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import type { GameView } from '@/game/engine';
import { HelpTip } from './help-tip';
import { faction } from '@/game/catalog';
import { leaderStrengthLabel } from '@/game/cards';
import { BattleComponentInspection } from './battle-component-inspection';
export function PrivateBattlePlan({ game }: { game: GameView }) {
const insight = game.battle?.fullPlanInsight;
if (
!insight ||
game.battle?.revealed ||
game.battle?.fullPlan?.owner !== game.me
)
return null;
const leader = game.allLeaders.find((l) => l.id === insight.plan.leader);
const name = (id: string | null) =>
id
? (insight.cards.find((c) => c.id === id)?.name ??
game.allLeaders.find((l) => l.id === id)?.name ??
id)
: 'None';
return (
<section
className="private-battle-plan"
aria-label="Private battle plan inspection"
>
<span className="eyebrow">Atreides · private inspection</span>
<h3>
{game.players.find((p) => p.id === insight.target)?.name}’s committed
plan <HelpTip topic="fullPlan" />
</h3>
<p className="muted">
Only you receive this inspection, including when you are helping an
ally. It stays available until both plans are publicly revealed. The
other combatant can prepare and seal their plan without an inspection
confirmation.
</p>
<dl>
<div>
<dt>Dial</dt>
<dd>{insight.plan.dial}</dd>
</div>
<div>
<dt>Spice support</dt>
<dd>{insight.plan.support}</dd>
</div>
<div>
<dt>Spice Banker commitment</dt>
<dd>{insight.plan.bankerSpice ?? 0}</dd>
</div>
<div>
<dt>Leader</dt>
<dd>{name(insight.plan.leader)}</dd>
</div>
<div>
<dt>Weapon</dt>
<dd>{name(insight.plan.weapon)}</dd>
</div>
<div>
<dt>Defense</dt>
<dd>{name(insight.plan.defense)}</dd>
</div>
<div>
<dt>Kwisatz Haderach</dt>
<dd>{insight.plan.kwisatz ? 'Included' : 'Not included'}</dd>
</div>
</dl>
<BattleComponentInspection
playerName={
game.players.find((p) => p.id === insight.target)?.name ?? 'Player'
}
cards={insight.cards}
leader={
leader
? {
name: leader.name,
factionName: faction(leader.faction).name,
strength: leaderStrengthLabel(leader),
}
: undefined
}
/>
</section>
);
}