Support owned Box and Kulon movement-promise preparation

Extend bounded Basic ground-movement reachability to held native CHOAM Kulon and owned paid Box recovery. Before payment, use only known current-discard identities. Preserve actual paid event, full inspection, one two-spice fee, original physical card replay and existing provisional full-hand exchange. Reject a Yes-breaking selection before the server shuffle without losing the paid search. Keep No-only searches on their normal decision before movement can end.

Record movement-relevant current-discard certainty at actual disposal, paid search, mixed claim and deck rebuild boundaries without projecting raw metadata or erasing historical memory. Use deterministic preparation on private copies, include preparation state in search memoization, protect fee-funded routes from voluntary spending and keep native Kulon counter semantics. Persistent busy-safe guidance and existing legal profiles use the actual next paid action; no strategy tuning.

Verify 17 files231/231, types/lint/build, three original-action control/table-rendering programmes and four existing profiles with40 accepted actions including neutral passes. Original public sources44/25 retain actual purchases1+1, shipment costs6/1, exhausted Hajr and native Kulon route4. Separate full-hand, legacy certainty, fee-loss and acquired-counter edges disclose conserved staging. Two prior-engine Auditor fixture failures are repaired by actual battle-result acknowledgements; test-only const lint is corrected.

Other timing, preparations/counter mitigation, unknown private possibilities, optional/Advanced, full modes, comprehensive assurance and deployed acceptance remain open. No saved-room/reset/restart/deployment operation.
This commit is contained in:
Quintine committed 2026-10-24 14:53:11 +11:00
1 parent edbcbe28de
commit b5a53da712
16 files changed
+772 -41

No files matched your search

+7 -2
View File
@@ -12,13 +12,18 @@ export function MovementPromises({ game, act, busy }: { game: GameView; act: (ac
const destination = next && game.combatLocations.find(location => location.id === next.territory)?.name;
const destinationLabel = destination ?? (typeof next?.territory === 'string' ? next.territory : 'the chosen destination');
const sector = typeof next?.sector === 'number' ? next.sector : 0;
const label = next?.type === 'card' ? 'Play Hajr to prepare the required movement'
const card = game.players.find(player => player.id === game.me)?.hand?.find(card => card.id === next?.card);
const recovered = game.nullentropy?.search?.cards.find(card => card.id === next?.card);
const label = next?.type === 'decision' ? `Choose ${recovered?.name ?? 'the required card'} in your paid Box search`
: next?.type === 'card' && card?.effect === 'nullentropyBox' ? 'Pay 2 spice and search with Nullentropy Box'
: next?.type === 'card' && next.mode === 'choam' ? 'Play Kulon to extend the required movement'
: next?.type === 'card' ? 'Play Hajr to prepare the required movement'
: next?.type === 'endMovement' ? 'Finish movement while honoring No'
: `${next?.movementCard ? 'Use Ornithopter and move' : 'Move'} ${amount} forces to ${destinationLabel}, sector ${sector}`;
return <section className="notice space-y-3" aria-label="Your ground-movement promises">
<h4>Your ground-movement promises</h4>
{own.map((promise, index) => <p key={index}>{promise.answer ? 'Yes: you must' : 'No: you must not'} {movementClaimText(promise)} before this Movement turn ends.</p>)}
<p className="fine">One qualifying move fulfills Yes. No remains binding through every later Hajr or Ornithopter move. Opposing effects release an answer only if it becomes impossible.</p>
<p className="fine">One qualifying move fulfills Yes. No remains binding through every later Hajr or Ornithopter move. A paid Box search must finish before moving; its fee is paid only once. Opposing effects release an answer only if it becomes impossible.</p>
{next && <Button className="game-action min-h-11 whitespace-normal" disabled={busy} onClick={() => { if (!busy) act(next); }}>{label}</Button>}
</section>;
}
+1 -1
View File
@@ -348,7 +348,7 @@ export function Truthtrance({
{kind === 'movement' ? (
<>
<MovementClaimFields id={`${factId}-movement`} value={movement} onChange={setMovement} />
<p className="fine">One ground movement of the chosen physical minimum, from now until the respondent’s Movement turn ends. Shipment, HMS relocation and Ambassador relocation do not count. Owned Hajr and Ornithopter can prepare a later move. No stays binding after earlier nonmatching moves.</p>
<p className="fine">One ground movement of the chosen physical minimum, from now until the respondent’s Movement turn ends. Shipment, HMS relocation and Ambassador relocation do not count. Owned Hajr, Ornithopter, native Kulon and paid Box recovery of a known discard can prepare a later move. No stays binding after earlier nonmatching moves.</p>
{!g.truthMovementTargets.includes(target) && <p className="notice">Choose the active Basic player after shipment and finish pending decisions. Optional-module and Advanced movement promises remain unfinished.</p>}
<p className="notice" aria-label="Public movement question preview">{truthQuestionText(question, leaderName, locationName)}</p>
</>