// The PUBLIC projection of a deduction case — what the server sends a client at connect (PURE shapes, // no secret truth). The server holds the full `DeductionCase` (with `condition`/`trueSource`/ // `incriminating`/`culpritDid`); the view leaks NONE of it (DEDUCTION.md §6). Crucially the CLUES are // NOT shipped here: a station's stack is revealed one slot at a time over #inspect→#revealed, so the // client can't see how deep a station goes (and can't tell working from sabotaged) until it earns each // reveal. The view just says who to question and which stations can be walked up to and investigated. import type { SuspectAttributes, Trait } from './types.js' /** A suspect as the client sees them: identity + visible attire (the only thing fragments quantify * over) + body facts. No secret state. `did` doubles as the key into the streamed world players, * whose `spriteRef` (`at:///actor.rpg.sprite/self`) renders them on the map. */ export interface SuspectView { did: string name: string bodyType?: string skinBucket?: string eyeBucket?: string worn: Trait[] held: Trait[] } /** The whole public case: who to question, which stations to investigate, where interviews happen. */ export interface DeductionCaseView { suspects: SuspectView[] /** Station ids the player can walk up to and `#inspect`. Includes clean (working) stations — the * client can't tell which bear clues; it asks the server, which reveals at most the frontier slot. */ inspectableStationIds: string[] /** Human labels for stations that authored one (`town.deception.stationName`, e.g. "Chicken coop"), * keyed by station id — used to make prompts read naturally. Absent id ⟹ fall back to generic wording. */ stationNames: Record /** The interview hub (the meeting/voting station); null when the map authors none. */ meetingStationId: string | null } /** Strip a suspect's attributes down to the public view (drops nothing secret today — attire is all * public — but centralizes the shape so the wire payload stays intentional). */ export function suspectView(s: SuspectAttributes): SuspectView { return { did: s.did, name: s.name, ...(s.bodyType ? { bodyType: s.bodyType } : {}), ...(s.skinBucket ? { skinBucket: s.skinBucket } : {}), ...(s.eyeBucket ? { eyeBucket: s.eyeBucket } : {}), worn: s.worn, held: s.held, } }