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.
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { DUKE_VIDAL_ID } from '@/game/duke-vidal';
|
|
import { CHEAP_HERO_TRAITOR } from '@/game/traitors';
|
|
import { parseFaceDancerFact, type FaceDancerFact } from '@/game/truthtrance-face-dancer';
|
|
|
|
export function FaceDancerFactFields({
|
|
value,
|
|
leaders,
|
|
onChange,
|
|
}: {
|
|
value: FaceDancerFact;
|
|
leaders: readonly { id: string; name: string }[];
|
|
onChange: (fact: FaceDancerFact) => void;
|
|
}) {
|
|
const identities = [
|
|
...leaders.filter((leader) => leader.id !== DUKE_VIDAL_ID && leader.id !== CHEAP_HERO_TRAITOR),
|
|
{ id: CHEAP_HERO_TRAITOR, name: 'Cheap Hero / Heroine' },
|
|
];
|
|
const allowed = identities.map((identity) => identity.id);
|
|
let error: string | null = null;
|
|
try { parseFaceDancerFact(value, allowed); }
|
|
catch (cause) { error = cause instanceof Error ? cause.message : 'Choose a printed Face Dancer identity.'; }
|
|
return (
|
|
<>
|
|
<label>
|
|
Face Dancer identity
|
|
<select
|
|
aria-label="Face Dancer identity"
|
|
value={value.leader}
|
|
aria-invalid={!!error}
|
|
onChange={(event) => {
|
|
try {
|
|
onChange(parseFaceDancerFact({ kind: 'faceDancer', leader: event.currentTarget.value }, allowed));
|
|
} catch { return; }
|
|
}}
|
|
>
|
|
{identities.map((identity) => (
|
|
<option key={identity.id} value={identity.id}>{identity.name}</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<p className="fine">
|
|
This asks whether the respondent currently holds an unrevealed Face
|
|
Dancer for this printed identity. A revealed, absent or returned card
|
|
means No.
|
|
</p>
|
|
<p className="fine">
|
|
This is a current fact at the answer, not a promise to reveal a Face
|
|
Dancer later. Ordinary traitor questions still ask only about ordinary
|
|
Traitor Cards.
|
|
</p>
|
|
<p className="fine">
|
|
Only the aggregate Truthtrance answer is published. This does not
|
|
physically reveal a Face Dancer or identify which part of an AND/OR
|
|
claim is true.
|
|
</p>
|
|
{error && <p className="notice" role="alert">{error}</p>}
|
|
</>
|
|
);
|
|
}
|