Support whole AND/OR ordinary bribe commitments

This commit is contained in:
Quintine committed 2026-10-26 15:53:57 +11:00
1 parent c81e00e6dd
commit 0e3c155876
21 files changed
+1778 -111

No files matched your search

+30 -1
View File
@@ -1,4 +1,7 @@
'use client';
import { BribeExpressionFields, bribeExpressionInputError } from './bribe-expression-fields';
import { bribeExpressionRecipients, type BribeExpression } from '@/game/bribe-expression-promises';
import { RevivalExpressionFields, revivalExpressionInputError } from './revival-expression-fields';
import type { RevivalExpression } from '@/game/revival-expression-promises';
import { LeaderRevivalClaimFields, leaderRevivalClaimInputError } from './leader-revival-claim-fields';
@@ -65,7 +68,7 @@ export function Truthtrance({
g.players.find((p) => p.id !== me.id)!.id,
);
const [kind, setKind] = useState<
'fact' | 'freeform' | 'battlePlan' | 'shipment' | 'mixedShipment' | 'movement' | 'traitorCall' | 'bid' | 'revival' | 'bribe' | 'alliance' | 'leaderRevival' | 'revivalExpression'
'fact' | 'freeform' | 'battlePlan' | 'shipment' | 'mixedShipment' | 'movement' | 'traitorCall' | 'bid' | 'revival' | 'bribe' | 'alliance' | 'leaderRevival' | 'revivalExpression' | 'bribeExpression'
>('fact');
const [revivalLeader, setRevivalLeader] = useState(g.allLeaders.find(leader =>
leader.faction === g.players.find(player => player.id === target)?.faction)?.id ?? '');
@@ -87,6 +90,12 @@ export function Truthtrance({
const bribeClaim: BribeClaim = { turn: g.turn, ...bribeSelection };
const bribeRespondent = g.players.find(player => player.id === target);
const bribeRecipients = g.players.filter(player => player.id !== target && player.id !== bribeRespondent?.ally);
const [bribeExpression, setBribeExpression] = useState<BribeExpression>({ kind: 'and', terms: [
{ kind: 'payment', recipient: g.me, minimum: 3 }, { kind: 'payment', recipient: '', minimum: 2 },
] });
const bribeExpressionError = bribeExpressionInputError(bribeExpression);
const validBribeExpressionRecipients = !bribeExpressionError && bribeExpressionRecipients(bribeExpression)
.every(recipient => bribeRecipients.some(player => player.id === recipient));
const [revivalSelection, setRevivalSelection] = useState<Pick<RevivalClaim, 'counter' | 'minimum'>>({ counter: 'total', minimum: 1 });
const revivalClaim: RevivalClaim = { turn: g.turn, ...revivalSelection };
const [bidMinimum, setBidMinimum] = useState(1);
@@ -133,6 +142,7 @@ export function Truthtrance({
: kind === 'revivalExpression' ? { kind, target, turn: g.turn, claim: revivalExpression }
: kind === 'leaderRevival' ? { kind, target, ...leaderRevivalClaim }
: kind === 'alliance' ? { kind, target, ...allianceClaim }
: kind === 'bribeExpression' ? { kind, target, turn: g.turn, claim: bribeExpression }
: kind === 'bribe' ? { kind, target, ...bribeClaim }
: kind === 'revival' ? { kind, target, ...revivalClaim }
: kind === 'bid' ? { kind, target, ...bidClaim }
@@ -264,6 +274,13 @@ export function Truthtrance({
{button(answer === 'unknown' ? 'I don’t know yet' : `Answer ${answer === 'yes' ? 'Yes' : 'No'}`, { type: 'truthAnswer', answer })}
</div>)}
</>
) : w.question!.kind === 'bribeExpression' ? (
<>
<p className="notice">Yes requires the whole AND/OR question to become true through your future ordinary payments. No keeps the whole expression false, and may permit some payments. Earlier payments and later recipient collection do not count. Keep the funds needed by every answer; late Mentat receipts wait for next turn&apos;s opening.</p>
{(g.truthBribeExpressionAnswers ?? []).map(answer => <div key={answer}>
{button(`Answer ${answer === 'yes' ? 'Yes' : answer === 'no' ? 'No' : 'I don’t know'}`, { type: 'truthAnswer', answer })}
</div>)}
</>
) : w.question!.kind === 'bribe' ? (
<>
<p className="notice">Yes requires cumulative ordinary bribe payments to this named recipient while possible. No keeps all later payments below the minimum. Earlier payments, gifts and allied aid do not count. You pay now; the recipient collects at the next Mentat opening. Payments during Mentat wait until next turn&apos;s opening.</p>
@@ -404,6 +421,7 @@ export function Truthtrance({
<option value="movement">Bind ground movement this turn</option>
<option value="alliance">Bind alliance membership at this Nexus close</option>
<option value="bribe">Bind ordinary bribe payments this turn</option>
<option value="bribeExpression">Combine ordinary bribe payments with AND/OR</option>
<option value="revivalExpression">Bind combined force and leader revivals</option>
<option value="leaderRevival">Bind a named leader revival this turn</option>
<option value="revival">Bind ordinary force revivals this turn</option>
@@ -448,6 +466,16 @@ export function Truthtrance({
{!g.truthAllianceTargets.includes(target) && <p className="notice">Choose a supported participant in a quiet open ordinary Nexus. Optional modules and broader alliance questions remain unfinished.</p>}
<p className="notice" aria-label="Public alliance question preview">{truthQuestionText(question, leaderName, locationName, name)}</p>
</>
) : kind === 'bribeExpression' ? (
<>
<BribeExpressionFields value={bribeExpression} recipients={bribeRecipients}
disabled={busy || !g.truthBribeExpressionTargets.includes(target)} onChange={setBribeExpression} />
<p className="fine">Count only ordinary payer payments after this answer until this turn ends. AND requires all conditions; OR permits any full alternative. No negates the whole expression. Original payment costs, non-allied recipients, earlier obligations and deferred receipts remain.</p>
{!g.truthBribeExpressionTargets.includes(target) && <p className="notice">Choose a supported participant before this turn ends and finish pending decisions.</p>}
<p className="notice" aria-label="Public combined bribe question preview">{bribeExpressionError
? bribeExpressionError : !validBribeExpressionRecipients ? 'Choose a non-allied recipient for every condition.'
: truthQuestionText(question, leaderName, locationName, name)}</p>
</>
) : kind === 'bribe' ? (
<>
<BribeClaimFields value={bribeClaim} recipients={bribeRecipients} disabled={busy || !g.truthBribeTargets.includes(target)}
@@ -573,6 +601,7 @@ export function Truthtrance({
(kind === 'revivalExpression' && (!!revivalExpressionInputError(revivalExpression) || !validRevivalExpressionNames || !g.truthRevivalExpressionTargets.includes(target))) ||
(kind === 'leaderRevival' && (!!leaderRevivalClaimInputError(leaderRevivalClaim, leaderRevivalChoices) || !g.truthLeaderRevivalTargets.includes(target))) ||
(kind === 'alliance' && (!!allianceClaimInputError(allianceClaim, alliancePartners) || !g.truthAllianceTargets.includes(target))) ||
(kind === 'bribeExpression' && (!!bribeExpressionError || !validBribeExpressionRecipients || !g.truthBribeExpressionTargets.includes(target))) ||
(kind === 'bribe' && (!!bribeClaimInputError(bribeClaim, bribeRecipients) || !g.truthBribeTargets.includes(target))) ||
(kind === 'revival' && (!!revivalClaimInputError(revivalClaim) || !g.truthRevivalTargets.includes(target))) ||
(kind === 'bid' && (!!bidClaimInputError(bidClaim) || !g.truthBidTargets.includes(target))) ||