Support Advanced Sapho Charity suspension

This commit is contained in:
Quintine committed 2026-10-27 23:36:27 +11:00
1 parent fcd93ca252
commit 70ef142577
19 files changed
+694 -49

No files matched your search

+3 -3
View File
@@ -17,7 +17,7 @@ export function CharityClaim(props: Props) {
const actor = game.me, turn = game.turn, order = game.charityOrder;
const currentActor = order?.remaining[0];
const currentName = game.players.find(player => player.id === currentActor)?.name ?? 'the current player';
const ownTurn = !order || (order.version === 1 || order.version === 2) && order.turn === turn && order.remaining[0] === actor;
const ownTurn = !order || (order.version === 1 || order.version === 2 || order.version === 3) && order.turn === turn && order.remaining[0] === actor;
const blocked = busy || !!game.truthtrance || !!game.nexusTraitors?.pending || game.charity.incomePending ||
!!game.response || !!game.decision || !!game.phaseOpening || !!game.automaticContinuationPending ||
!ownTurn || !!game.charity.orderBlocked;
@@ -26,8 +26,8 @@ export function CharityClaim(props: Props) {
return (
<>
{order && <div className="fine" data-testid="charity-turn-status">
<p>{order.version === 2 ? 'Juice of Sapho changed the remaining Charity order. Completed turns stay finished.' : 'Advanced Charity proceeds from the First Player counterclockwise, one player at a time.'}</p>
<p>{(order.version !== 1 && order.version !== 2) || order.turn !== turn
<p>{order.version === 2 || order.version === 3 ? 'Juice of Sapho changed the remaining Charity order. Completed turns stay finished.' : 'Advanced Charity proceeds from the First Player counterclockwise, one player at a time.'}</p>
<p>{(order.version !== 1 && order.version !== 2 && order.version !== 3) || order.turn !== turn
? 'This Charity turn order is no longer current.'
: !currentActor ? 'All Charity turns are complete.'
: !order.remaining.includes(actor) ? `Your Charity turn is complete. Waiting for ${currentName} to finish Charity.`
+6 -4
View File
@@ -18,7 +18,7 @@ function unavailableReason(game: GameView) {
if (game.phase === 2)
return !game.advanced || !game.charityOrder
? 'Charity order changes require an ordered Advanced Charity phase. Basic and an already-opened unstamped phase keep their existing timing.'
: 'Choose first or last only among remaining unstarted Charity turns. A player who has claimed finishes their Charity turn unchanged. Completed turns stay finished; already-held positions and committed cards are unavailable.';
: 'Change the order of remaining Charity turns. Another player finishes any started turn first. At a clean boundary, you may pause your own started turn and resume it last with your claim unchanged. Completed turns stay finished; already-held positions and committed cards are unavailable.';
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.'
@@ -67,7 +67,7 @@ export function SaphoOrderButton({
}}
>
{option.scope === 'battleAggressor' ? 'Become aggressor and discard Juice of Sapho' :
(option.scope === 'movement' || option.scope === 'revival') && option.resumeOwn ? `Resume your ${option.scope} last and discard Juice of Sapho` :
(option.scope === 'movement' || option.scope === 'revival' || option.scope === 'charity') && option.resumeOwn ? `Resume your ${option.scope} last 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' : option.scope === 'charity' ? 'Take your Charity turn' : 'Take your movement turn'} ${option.mode} and discard Juice of Sapho`}
</Button>
);
@@ -114,7 +114,7 @@ export function JuiceOfSapho({
const extortion = option.scope === 'extortion';
const revival = option.scope === 'revival';
const charity = option.scope === 'charity';
const resumeOwn = (option.scope === 'movement' || revival) && option.resumeOwn;
const resumeOwn = (option.scope === 'movement' || revival || charity) && option.resumeOwn;
const afterCurrent = (option.scope === 'movement' || revival || charity) && option.afterCurrent;
const currentPlayer = afterCurrent
? game.players.find(player => player.id === afterCurrent)?.name ?? 'The active player'
@@ -154,7 +154,9 @@ export function JuiceOfSapho({
))}
<p>
{resumeOwn
? revival
? charity
? 'Pause your started Charity turn and resume it last, after the other remaining players. Your completed claim and its payment remain unchanged. This timing is provisional.'
: revival
? 'Pause your started Revival turn and resume it last, after the other remaining players. Returned pieces, costs and used revival allowances remain unchanged. This timing is provisional.'
: 'Pause your started turn and resume it last, after the other remaining players. Shipment and movement already used stay spent; no extra action is granted. This timing is provisional.'
: aggressor
+1 -1
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.version !== 2) || order.turn !== game.turn || game.status !== 'playing' || game.phase !== 4)
if ((order.version !== 1 && order.version !== 2 && order.version !== 3) || 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.';