Open native advisor preparation before the first shipment

Allow Advanced BG preparation in phases2–5 at quiet boundaries, preserving
original phase/auction/readiness and native eligibility. Close a current-turn
receipt only on committed shipments, including marker and Sneak Attack entry.
Retain the legacy phase5 queue and qualify the per-territory prevention policy.
Connect persistent controls, minimal bot support and focused native evidence.
Complete modes and browser/deployed acceptance remain open.
This commit is contained in:
Quintine committed 2026-10-24 18:36:55 +11:00
1 parent 0e3d6a250e
commit 660b0a0c25
18 files changed
+1269 -13

No files matched your search

+72
View File
@@ -0,0 +1,72 @@
'use client';
import { ArrowRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { territory } from '@/game/board';
import type { Action, GameView } from '@/game/engine';
export function AdvisorPreparation({
game,
act,
busy,
}: {
game: GameView;
act: (action: Action) => void;
busy: boolean;
}) {
const offer = game.advisorPreparation;
const me = game.players.find((player) => player.id === game.me);
if (!offer || game.status !== 'playing' || !game.advanced ||
game.phase < 2 || game.phase > 5 || me?.faction !== 'beneGesserit') return null;
const priorityPending = !!game.response || !!game.decision || !!game.phaseOpening;
const blocked = offer.blocked || (priorityPending
? 'Finish the current response, decision or phase opening before preparing advisors.'
: null);
const disabled = busy || !!blocked || !!game.truthtrance || !!game.nexusTraitors?.pending;
return (
<section className="notice space-y-3" aria-label="Bene Gesserit advisor preparation">
<h3 className="font-serif text-xl">Prepare advisors for battle</h3>
<p className="muted">
After Spice Blow and any Nexus, you may prepare advisors until the first
actual shipment has occurred. This window includes Charity, Bidding, Revival
and the opening of Shipment and Movement.
</p>
<p className="fine">
Choose a territory to turn all your advisors there into fighters, across
every sector. Storm, ordinary allied occupation and fresh-advisor restrictions
still apply. The existing supported Ecaz co-side composition retains its
separate eligibility.
</p>
<p className="fine">
Provisional Karama policy: a canceled preparation blocks that territory
until this window ends. Other eligible territories remain available.
Intrusion, Nexus conversion and automatic advisor flips remain independent.
</p>
<p className="fine">
The ordinary advisor chooser during Shipment and Movement remains
available. Declining that choice does not close this preparation window.
</p>
{blocked && <p className="notice" id="advisor-preparation-unavailable">{blocked}</p>}
{!offer.territories.length && (
<p className="muted">No advisor territory is currently eligible for preparation.</p>
)}
<div className="flex flex-wrap gap-2">
{offer.territories.map((id) => (
<Button
key={id}
data-testid={`advisor-preparation-${id}`}
className="game-action min-h-11 whitespace-normal"
disabled={disabled}
aria-describedby={blocked ? 'advisor-preparation-unavailable' : undefined}
onClick={() => {
if (!disabled) act({ type: 'prepareAdvisors', territory: id });
}}
>
Prepare advisors · {territory(id).name}
<ArrowRight size={15} />
</Button>
))}
</div>
</section>
);
}
+5
View File
@@ -53,6 +53,7 @@ import { TerrorBoardMarkers } from './terror-board-markers';
import { AmbassadorBoardMarkers } from './ambassador-board-markers';
import { CharityClaim } from './charity-claim';
import { FremenRevivalGrant } from './fremen-revival-grant';
import { AdvisorPreparation } from './advisor-preparation';
import { homeworldRevivalActionBlock } from '@/game/homeworld-revival-deployment-options';
import { GuildHomeworldShipment } from './guild-homeworld-shipment';
import { JunctionTransport } from './junction-transport';
@@ -1973,6 +1974,7 @@ export function GameTable({
{!reactionOwnsControls && <>
<NexusCards game={g} act={act} busy={busy} />
<Recruits game={g} act={act} busy={busy} />
<AdvisorPreparation game={g} act={act} busy={transportBusy || !!g.roomControl?.paused || !!g.roomControl?.closed || !!me.autopilot} />
<NexusTraitors game={g} act={act} busy={transportBusy || (!!g.roomControl?.paused || !!g.roomControl?.closed) || !!me.autopilot} />
<DiscoveryPanel game={g} act={act} busy={transportBusy || (!!g.roomControl?.paused || !!g.roomControl?.closed) || !!me.autopilot} />
<LeaderSkillsPanel skills={g.leaderSkills} leaders={g.allLeaders} players={g.players} act={act} busy={transportBusy || (!!g.roomControl?.paused || !!g.roomControl?.closed) || !!me.autopilot} />
@@ -2548,6 +2550,9 @@ export function GameTable({
allow five forces.
</p>
)}
{g.response.kind === 'advisorFlip' && g.response.advisorResume === 'preparation' && (
<p className="notice">Bene Gesserit declared native advisor preparation before the first shipment. Karama can prevent this territory’s flip. The current phase and its original auction, readiness or movement control continue afterward.</p>
)}
{g.response.kind === 'fremenRevivalGrant' && (
<p className="notice">
Fremen offered their current ally the ordinary free-revival rate of three.