Support Sapho ordering during Advanced Revival

This commit is contained in:
Quintine committed 2026-10-26 10:48:38 +11:00
1 parent 011b6aa096
commit 82370d906f
15 files changed
+1092 -28

No files matched your search

+22 -5
View File
@@ -15,6 +15,10 @@ function unavailableReason(game: GameView) {
if (game.truthtrance || game.response || game.phaseOpening ||
(game.decision && !(game.phase === 8 && game.decision.kind === 'moritaniExtortion')))
return 'Finish the current interaction before changing turn order.';
if (game.phase === 4)
return !game.advanced || !game.revivalOrder
? 'Revival order changes require an ordered Advanced Revival phase. Basic and an already-opened unstamped phase keep their existing timing.'
: 'Choose first or last only among remaining unstarted Revival turns. A player who has started finishes their Revival choices unchanged. Completed turns stay finished; already-held positions and committed cards are unavailable.';
if (game.phase === 8 && game.extortion?.pending)
return 'Only an unanswered non-Moritani player can change their Extortion choice position. Earlier declines stay finished. Already-held first/last positions and committed cards are unavailable; you may still decline payment when your choice arrives.';
if (game.phase === 3) {
@@ -59,7 +63,7 @@ export function SaphoOrderButton({
}}
>
{option.scope === 'battleAggressor' ? 'Become aggressor and discard Juice of Sapho' :
`${bidding ? 'Bid' : option.scope === 'battleOrder' ? 'Choose battles' : option.scope === 'extortion' ? 'Take your Extortion choice' : 'Take your movement turn'} ${option.mode} and discard Juice of Sapho`}
`${bidding ? 'Bid' : option.scope === 'battleOrder' ? 'Choose battles' : option.scope === 'extortion' ? 'Take your Extortion choice' : option.scope === 'revival' ? 'Take your Revival turn' : 'Take your movement turn'} ${option.mode} and discard Juice of Sapho`}
</Button>
);
}
@@ -79,7 +83,9 @@ export function JuiceOfSapho({
(c) => richeseCardDefinition(c)?.card.effect === 'juiceOfSapho',
);
if (!card) return null;
const options = game.saphoOptions ?? [];
const options = (game.saphoOptions ?? []).filter(option => option.scope !== 'revival' ||
(game.advanced && game.phase === 4 && game.revivalOrder?.remaining.includes(game.me) &&
!game.revivalOrder.completed.includes(game.me)));
return (
<details className="my-4 min-w-0">
<summary className="min-h-11 cursor-pointer py-3">
@@ -98,7 +104,8 @@ export function JuiceOfSapho({
const battle = option.scope === 'battleOrder';
const aggressor = option.scope === 'battleAggressor';
const extortion = option.scope === 'extortion';
const afterCurrent = option.scope === 'movement' && option.afterCurrent;
const revival = option.scope === 'revival';
const afterCurrent = (option.scope === 'movement' || revival) && option.afterCurrent;
const currentPlayer = afterCurrent
? game.players.find(player => player.id === afterCurrent)?.name ?? 'The active player'
: null;
@@ -107,7 +114,15 @@ export function JuiceOfSapho({
key={`${option.scope}-${option.event}-${option.mode}`}
className="flex min-w-0 flex-col gap-2"
>
{currentPlayer && (
{currentPlayer && (revival ? (
<p aria-label="Provisional queued Revival order">
Provisional queued Revival order: {currentPlayer} finishes
the current Revival turn unchanged. Your {option.mode}
{' '}position applies among later unstarted Revival turns.
Completed turns, returned pieces, payments and revival
allowances stay unchanged.
</p>
) : (
<p>
Provisional queued movement order: {currentPlayer} finishes
the current combined shipment and movement turn unchanged.
@@ -118,7 +133,7 @@ export function JuiceOfSapho({
? ' The Advanced Guild keeps its native timing choice at the next boundary. A last position still follows the Guild.'
: ''}
</p>
)}
))}
<p>
{aggressor
? 'Become this battle’s aggressor. You win ordinary ties; a Habbanya Stronghold advantage still takes precedence. The participants, committed plans and later battle choices stay unchanged.'
@@ -130,6 +145,8 @@ export function JuiceOfSapho({
? `Bid ${option.mode} in the current Once Around lot. Each eligible player still has only one bidding opportunity.`
: battle
? `Choose your remaining battles ${option.mode}. Completed battles stay resolved. This changes who chooses next, not the aggressor or tie advantage of a battle. Other players may still choose battles against you before your turn.`
: revival
? `Take the ${option.mode} remaining Revival turn. Completed Revival turns stay finished. This changes the remaining order without granting another revival, resetting allowances or returning completed players to the queue.`
: `Take the ${option.mode} remaining combined shipment and movement turn. Completed turns stay completed${option.mode === 'last' ? ', and the Guild cannot move behind you' : game.advanced && game.players.some(player => player.faction === 'guild' && player.id !== game.me) ? '. Provisional first priority: the Guild retains its next legal timing choice and may take its turn before yours; a Karama-locked privilege stays locked' : ''}.`}
</p>
<SaphoOrderButton card={card.id} option={option} act={act} busy={busy} />
+4 -3
View File
@@ -10,7 +10,7 @@ type Props = { game: GameView; act: (action: Action) => void; busy: boolean };
export function revivalTurnOrderBlock(game: GameView): string | null {
const order = game.revivalOrder;
if (!game.advanced || !order) return null;
if (order.version !== 1 || order.turn !== game.turn || game.status !== 'playing' || game.phase !== 4)
if ((order.version !== 1 && order.version !== 2) || order.turn !== game.turn || game.status !== 'playing' || game.phase !== 4)
return 'This Revival turn is no longer current.';
if (!game.players.some(player => player.id === game.me) || !order.original.includes(game.me))
return 'This seat has no Revival turn in the current order.';
@@ -62,8 +62,9 @@ export function RevivalTurn({ game, act, busy }: Props) {
<p>Completed Revival turns: {order.completed.map(name).join(', ') || 'None yet'}.</p>
<p className="fine" aria-label="Provisional Revival grouping">
Provisional grouping: finish your force, leader and Kwisatz Haderach revival
choices together before ending your turn. Revival turns follow the printed
storm order for forces. Tleilaxu can still answer another player’s requests.
choices together before ending your turn. Revival turns begin in the printed
storm order for forces. Juice of Sapho may change the remaining order;
completed Revival turns stay finished. Tleilaxu can still answer another player’s requests.
</p>
{blocked && <output className="fine block">{blocked}</output>}
<FinishRevivalTurn game={game} act={act} busy={busy} />