Honor group alliances in Truthtrance
Verify and publish checkpoint container / container (push) Successful in 11m22s

This commit is contained in:
Quintine committed 2026-10-29 00:41:56 +11:00
1 parent 9cb65a21f5
commit 3e0fa2a8a2
11 files changed
+592 -24

No files matched your search

+13 -7
View File
@@ -62,6 +62,7 @@ import { leaders } from './cards';
import { combatLocationName } from './combat-location';
import { parseForceCountFact, forceCountFactMatches, forceCountFactText, forceCountCounterError, type ForceCountFact, type ForceCountPlayer } from './truthtrance-force-count';
import type { Action, Game, Player } from './engine';
import { coalitionAllies } from './coalition-alliance';
import { TRUTH_CARD_NAMES } from './truthtrance-card-names';
import { CHEAP_HERO_TRAITOR } from './traitors';
import {
@@ -258,6 +259,11 @@ function battleLeaderIdentities(g: Game) {
...(g.dukeVidal?.leader.id === DUKE_VIDAL_ID ? [g.dukeVidal.leader] : []),
];
}
function nonAlliedBribeRecipient(g: Game, respondent: Player, recipient: string): boolean {
return recipient !== respondent.id && recipient !== respondent.ally &&
g.players.some(player => player.id === recipient) &&
(!g.coalitionState || !coalitionAllies(g.players, g.coalitionState, respondent.id).includes(recipient));
}
function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
const v = record(value);
check(
@@ -341,8 +347,7 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
const respondent = g.players.find(player => player.id === v.target)!;
const mixed = parseMixedActionExpression(v.mixed, fact => parseFact(g, fact, respondent), input => {
const action = parseBribePaymentClaim(input);
check(action.recipient !== respondent.id && action.recipient !== respondent.ally &&
g.players.some(player => player.id === action.recipient), 'Choose another current non-allied ordinary bribe recipient.');
check(nonAlliedBribeRecipient(g, respondent, action.recipient), 'Choose another current non-allied ordinary bribe recipient.');
return action;
});
return { kind: 'mixedBribe', target: v.target as string, turn: g.turn, mixed };
@@ -354,7 +359,7 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
const respondent = g.players.find(player => player.id === v.target)!;
check(bribePromiseModeSupported(g) && g.status === 'playing' && g.phase >= 0 && g.phase <= 8 &&
claim.turn === g.turn && bribeExpressionRecipients(claim.claim).every(recipient =>
recipient !== respondent.id && recipient !== respondent.ally && g.players.some(player => player.id === recipient)) &&
nonAlliedBribeRecipient(g, respondent, recipient)) &&
!g.response && !g.decision && !g.phaseOpening && !g.pendingTreacheryDiscard && !g.pendingKarama,
'Choose current-turn ordinary bribe statements to non-allied participants in a supported table.');
return { kind: 'bribeExpression', target: v.target, ...claim };
@@ -364,8 +369,8 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
'Supply the current turn, named bribe recipient, minimum and respondent.');
const claim = parseBribeClaim(v), respondent = g.players.find(player => player.id === v.target)!;
check(bribePromiseModeSupported(g) && g.status === 'playing' && g.phase >= 0 && g.phase <= 8 &&
claim.turn === g.turn && claim.recipient !== respondent.id && claim.recipient !== respondent.ally &&
g.players.some(player => player.id === claim.recipient) && !g.response && !g.decision && !g.phaseOpening &&
claim.turn === g.turn && nonAlliedBribeRecipient(g, respondent, claim.recipient) &&
!g.response && !g.decision && !g.phaseOpening &&
!g.pendingTreacheryDiscard && !g.pendingKarama,
'Choose a current-turn ordinary bribe to another non-allied participant in a supported table.');
return { kind: 'bribe', target: v.target, ...claim };
@@ -482,8 +487,8 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
'Choose the original owned native Voice or Prescience declaration opportunity.');
}
}
if (action.kind === 'payment') check(action.recipient !== respondent.id && action.recipient !== respondent.ally &&
g.players.some(player => player.id === action.recipient), 'Choose another current non-allied ordinary bribe recipient.');
if (action.kind === 'payment') check(nonAlliedBribeRecipient(g, respondent, action.recipient),
'Choose another current non-allied ordinary bribe recipient.');
if (action.kind === 'bid') check(bidPromiseModeSupported(g) && g.phase === 3 && g.auction &&
!g.currentAuctionSale && !g.richeseAuction && !g.ixAuction && action.lot === g.auction.index + 1,
'Choose the current ordinary bid lot in a supported native auction.');
@@ -491,6 +496,7 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
action.leader === 'kwisatz' && g.advanced && respondent.faction === 'atreides',
'Choose a personal native faction leader, or the Advanced Atreides Kwisatz Haderach.');
if (action.kind === 'alliance') check(alliancePromiseModeSupported(g) && g.phase === 1 && g.nexus &&
!g.coalitionState?.groups.some(group => group.members.length >= 3) &&
!g.spiceWindow && !g.spiceResolution && action.partner !== respondent.id &&
g.players.some(player => player.id === action.partner),
'Choose another seated final alliance partner in the current quiet native Nexus.');