Files
Dune/game/truthtrance-face-dancer.ts
Quintine 6ea691255a Add verified current Face Dancer questions to Truthtrance
Ask about a currently held unrevealed printed identity through shared facts
and AND/OR authoring. Keep ordinary Traitor meaning separate, candidate
identities public and only the aggregate answer published. Existing answer,
physical disposal and continuation flow need no new AI strategy.

Verified: 12 affected offline files86/86, types/lint and production build;
twelve actual field/question/gameplay/GameTable SSR journeys and sixteen legal
profile answers. Native Basic/Advanced sources and actual Mentat replacement
prove a current Yes creates no future reveal or retention promise.

Existing mixed-shipment admission stays unchanged. No saved-room, D1, reset,
restart or deployment operation; broader Truthtrance/mode assurance remains.
2026-10-24 19:15:46 +11:00

40 lines
1.8 KiB
TypeScript

import { DUKE_VIDAL_ID } from './duke-vidal';
import { CHEAP_HERO_TRAITOR } from './traitors';
/** A current unrevealed card identity, never a promise to retain or reveal it. */
export type FaceDancerFact = { kind: 'faceDancer'; leader: string };
/** The caller supplies this game's canonical physical Traitor identities. */
export function parseFaceDancerFact(
value: unknown,
allowedIdentities: readonly string[],
): FaceDancerFact {
if (!value || typeof value !== 'object' || Array.isArray(value))
throw new Error('Choose a current unrevealed Face Dancer identity fact.');
const input = value as Record<string, unknown>;
const keys = Reflect.ownKeys(input);
if (keys.length !== 2 || !keys.includes('kind') || !keys.includes('leader') ||
input.kind !== 'faceDancer')
throw new Error('Choose only a current unrevealed Face Dancer identity fact.');
if (typeof input.leader !== 'string' || !input.leader.trim() ||
input.leader === DUKE_VIDAL_ID || !allowedIdentities.includes(input.leader))
throw new Error('Choose a physical Traitor identity in this game for the Face Dancer fact.');
return { kind: 'faceDancer', leader: input.leader };
}
/** Ordinary Traitor cards and already revealed or returned Face Dancers do not count. */
export function faceDancerFactMatches(
holder: { faceDancers?: readonly { leader: string; revealed: boolean }[] },
fact: FaceDancerFact,
): boolean {
return holder.faceDancers?.some(card => card.leader === fact.leader && card.revealed === false) ?? false;
}
export function faceDancerFactText(
fact: FaceDancerFact,
leaderName: (id: string) => string,
): string {
const name = fact.leader === CHEAP_HERO_TRAITOR ? 'Cheap Hero / Heroine' : leaderName(fact.leader);
return `you currently hold ${name} as an unrevealed Face Dancer`;
}