Protect Sapho priority at the Testing Station
Verify and publish checkpoint container / container (push) Successful in 11m16s
Verify and publish checkpoint container / container (push) Successful in 11m16s
This commit is contained in:
1 parent
f73a6e0a9c
commit
a6be17fc5d
12 files changed
+391
-9
No files matched your search
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
import { SaphoArrivalOrder } from './sapho-arrival-order';
|
||||
import { SaphoStationPriority } from './sapho-station-priority';
|
||||
import { RicheseBidPromisePanel } from './richese-bid-promise-panel';
|
||||
import { EarlyRicheseBidPromises } from './early-richese-bid-promises';
|
||||
import { TravelPromises } from './travel-promises';
|
||||
@@ -391,7 +392,7 @@ export function GameTable({
|
||||
const harkonnenBetrayalOwnsControls = !!g.nexusHarkonnenBetrayalReaction &&
|
||||
!g.response && !g.decision && !g.truthtrance && !g.phaseOpening &&
|
||||
!g.automaticContinuationPending && !g.nexusCards?.waiting.length && !g.nexusTraitors?.pending;
|
||||
const busy = reactionBusy || !!g.saphoArrivalOrder || !!g.victoryContinuation || !!g.nexusFremenRideBetrayal || g.automaticContinuationPending || !!g.semutaReaction || !!g.kullReaction || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction || !!g.nexusIxianReplacement || ixianBetrayalOwnsControls || harkonnenBetrayalOwnsControls;
|
||||
const busy = reactionBusy || !!g.saphoStationPriority || !!g.saphoArrivalOrder || !!g.victoryContinuation || !!g.nexusFremenRideBetrayal || g.automaticContinuationPending || !!g.semutaReaction || !!g.kullReaction || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction || !!g.nexusIxianReplacement || ixianBetrayalOwnsControls || harkonnenBetrayalOwnsControls;
|
||||
const reactionOwnsControls = !!g.saphoArrivalOrder || !!g.nexusFremenRideBetrayal || !!g.kullReaction || !!g.kullCounterEvent || !!g.richeseBetrayalReaction || !!g.guildBetrayalReaction || !!g.nexusIxianReplacement || ixianBetrayalOwnsControls || harkonnenBetrayalOwnsControls;
|
||||
const canSpendKarama = (card: Card, purpose: KullPurpose) =>
|
||||
!kullPurposeBlock(g, purpose) && canUseAsKaramaRole(g, me, card);
|
||||
@@ -1535,6 +1536,8 @@ export function GameTable({
|
||||
<EcazLoyaltyCard loyalty={g.ecazLoyalty} />
|
||||
<MoritaniTerrorSupply game={g} />
|
||||
<MoritaniExtortionNotice game={g} />
|
||||
{g.saphoStationPriority && <SaphoStationPriority game={g} act={act}
|
||||
busy={transportBusy || !!g.roomControl?.paused || !!g.roomControl?.closed || !!me.autopilot} />}
|
||||
{g.saphoArrivalOrder && <SaphoArrivalOrder view={g.saphoArrivalOrder} me={g.me}
|
||||
busy={transportBusy || !!g.roomControl?.paused || !!g.roomControl?.closed || !!me.autopilot}
|
||||
name={id => g.players.find(player => player.id === id)?.name ?? id} act={act} />}
|
||||
@@ -6116,7 +6119,9 @@ export function GameTable({
|
||||
<EcazSpecialKarama quote={g.ecazSpecialKarama ?? null} act={act} busy={busy} />
|
||||
<RicheseGift game={g} act={act} busy={busy} />
|
||||
<Distrans game={g} act={act} busy={busy} />
|
||||
<JuiceOfSapho game={g} act={act} busy={busy} />
|
||||
<JuiceOfSapho game={g} act={act} busy={g.saphoStationPriority
|
||||
? transportBusy || !!g.roomControl?.paused || !!g.roomControl?.closed || !!me.autopilot || !g.saphoStationPriority.canPass
|
||||
: busy} />
|
||||
<NullentropyBox game={g} act={act} busy={busy} />
|
||||
<ResidualPoison game={g} act={act} busy={busy} />
|
||||
<PortableSnooper game={g} act={act} busy={busy} />
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import type { Action } from '@/game/engine';
|
||||
import { Button } from './ui/button';
|
||||
|
||||
/** This is the public opportunity projected for this viewer, independent of
|
||||
* whether any player holds a particular card. */
|
||||
export type SaphoStationPriorityView = {
|
||||
event: string;
|
||||
canPass: boolean;
|
||||
waiting: boolean;
|
||||
};
|
||||
|
||||
export type SaphoStationPriorityProps = {
|
||||
game: { saphoStationPriority: SaphoStationPriorityView | null };
|
||||
act: (action: Action) => void;
|
||||
busy: boolean;
|
||||
};
|
||||
|
||||
/** Keep the opportunity visible until the server advances the Station order.
|
||||
* Actual order-changing choices remain in the player's own card controls. */
|
||||
export function SaphoStationPriority({ game, act, busy }: SaphoStationPriorityProps) {
|
||||
const view = game.saphoStationPriority;
|
||||
if (!view) return null;
|
||||
const event = view.event;
|
||||
const canAct = () => !busy && game.saphoStationPriority === view &&
|
||||
view.event === event && typeof event === 'string' && event.length > 0 && view.canPass;
|
||||
|
||||
return <section className="notice space-y-3" aria-label="Testing Station priority" aria-live="polite">
|
||||
<h3>Testing Station priority</h3>
|
||||
<p>Before the next Testing Station choice, players may use an available order change from their own hand. Already completed choices stay finished.</p>
|
||||
{view.canPass ? <Button className="game-action min-h-11 whitespace-normal"
|
||||
data-testid="sapho-station-priority-pass" disabled={!canAct()}
|
||||
onClick={() => { if (canAct()) act({ type: 'saphoStationPass', event }); }}>
|
||||
Continue without changing the order
|
||||
</Button> : <p>{view.waiting ? 'Waiting for the other players to continue.' : 'Your response has been recorded.'}</p>}
|
||||
</section>;
|
||||
}
|
||||
Reference in new issue
Block a user