Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dialogue architecture

Settlement dialogue is the markerless discovery boundary for local problems. Inns surface unknown unresolved symptoms; overview is fallback only when no inn NPC is available. Locals repeat referrals. Hidden causes and destinations stay private.

Publicly notorious recurring hostile cases are the exception to investigation secrecy. An eligible innkeeper, or an explicitly capable organization chapter representative speaking to a dues-current member, may state the canonical threat, exact site, and approximate count band. This is one shared authoritative dialogue disclosure; it carries no testimony, evidence, preparation advice, or client-selected case ID. The disclosure upserts a durable observer journal case and exact public-alias pin; later referrals refresh the count without adding a second entry.

Witness questioning uses proposition-granular authority. Hearing each atomic claim automatically creates a private, fallible Insight assessment for that observer. The gateway exposes only an opaque challenge token, the exact displayed claim boundary, and a bounded unknown, likely_false, or likely_true presentation signal. It never exposes reliability, canonical truth, proposition identity, rolls, thresholds, or correctness. The testimony draft separately authors the exact challengeable substring and may author any subset of Charm, Command, and Bluff lines. Present lines must be nonempty and unique; the client has no generic fallback. Surrounding narration and punctuation remain ordinary text, and speaker attribution replaces redundant phrases such as “The witness says.” Insight reads demeanor rather than acting as supernatural fact detection: sincere mistakes lean the same way as sincere accurate testimony, deliberate deception leans the other way, and evasive or partly truthful accounts have no private directional signal. Noise can still make every assessment wrong.

Each fresh NPC encounter may issue a private, dialogue-session-scoped witness social capability for that participant, but the gateway does not project it until the observer actually selects and hears that witness's quest testimony in the current session. Generic greetings and ordinary conversation therefore show no claim controls. Once engaged, each highlighted claim is a real accessible control, including green and uncertain claims. Activating one opens its local Charm, Command, and Bluff responses immediately below the utterance. Relationship, familiarity, and demeanor remain in the normal social popup. Hidden concern binding, diagnostic correctness, personality fit, checks, rolls, and chances remain private. Hearing testimony spends no additional time for Insight. Charm is the lowest-risk and lowest-leverage response, Command is medium and always strains affinity, and Bluff is highest-risk and highest-leverage. Each response spends five strategic minutes. A claim can receive at most one response; other claims remain actionable. Action receipts and session revisions make retries idempotent and stale requests fail closed.

Ordinary conversation is deliberately outside the quest dialogue controls. The normal social menu offers a duration-selectable chat with a present local; claim responses appear only in the relevant active dialogue session after that witness's quest testimony is heard. Casual chat can still change that NPC's private morale, directional affinity, and familiarity, so time spent getting to know someone can affect a later confrontation without revealing whether they have quest information.

A challenge succeeds only when that particular claim is factually inaccurate and its social check succeeds. Accurate claims and insufficient checks share the same safe failure wording. Success may release only the canonical withheld testimony already authored for the exact witness; released testimony follows the same structured claim and passive-assessment path. The response shows only the realized clamped affinity change, never the exact relationship value.

Persistent settlement actors

Settlement dialogue is authorized against persistent settlement_npc identities and their authoritative strategic settlement_npc_presence, rather than a client-created <settlement>:<service> name. A location may contain several NPCs; changing the addressed portrait changes the actor while the character remains at that location. Service providers retain their service conversation, while ordinary residents use the compiled local-resident conversation and cannot receive service-only topics.

The public NPC row contains only visible identity and presentation: name, age band, presentation, height, build, hair/facial hair, complexion, visible features, clothing, profession, household, and local role. Private demographic sex, the internal projection key, population seed explanations, and relation weights remain private. Dialogue facts include typed age, profession, status, clothing presence, prior interaction, language compatibility, observable location role, and time period. Hidden causal circumstances remain private until a future discovery system deliberately reveals them. Greeting response priority remains deterministic.

Population choices use contextual weighted relations. Zero plausibility is a hard exclusion; low positive values remain rare. Curation weight stays separate from world plausibility, and unusual demographic/location combinations require a causal bridge. One relation owns each conditional weight; inverse tables are not duplicated. Production population creation calls the canonical typed evaluator in adventuresim-core, and its private serialized explanation records the input context and every selected relation, factor, decision, and required bridge.

Scripted dialogue is a compiled, server-authoritative strategic system. It is separate from free-form local chat. Authors edit the JSON-compatible subset of YAML in content/dialogue/*.yaml; builds validate and embed a deterministic catalog, its SHA-256 revision, and compiler-derived source locations. Runtime servers do not read loose content files.

Authoring model

The distinction between generic and quest dialogue is authority, not a separate rendering system. A direct topic response may contain the typed runtime: testimony binding when—and only when—the same response applies receive_referred_testimony. For the exact generated witness, the server expands that slot through the normal turn pipeline into persisted text, claim, and text fragments. A claim fragment transports only its displayed value and event-local order. Proposition identity, reliability, factual accuracy, demeanor, checks, and rolls never enter event JSON. The private assessment row must match the exact session event, claim order, and displayed text before the gateway makes the fragment interactive; a missing or mismatched row leaves ordinary inert text. period_claim remains display-only and literal YAML cannot manufacture claim authority. Conversation-start responses and prompt result turns cannot contain authoritative testimony because those execution paths do not carry the exact emitted claim-event sequence into the receiving effect.

Each conversation has a stable ID and named participant roles. A role declares player or npc plus minimum/maximum cardinality, so one authored exchange can require a shopkeeper and assistant or address several players. Optional on_start responses use the same conditions, priority rules, attributed turns, effects, and automatic source mapping as topic responses. The server evaluates one start response exactly once when it creates a session; use it for greetings instead of making the browser select a topic implicitly. Topics have stable IDs, labels, knowledge/eligibility conditions, and explicitly prioritized responses. A response contains attributed turns composed of text and inline topic fragments. It may also contain an allowlisted typed runtime slot for a speaker's visible identity, place, symptom, claim, uncertainty, referral, evidence, testimony, or contract terms. Authored literals and runtime slots remain distinct in the compiled catalog and source map. The server resolves slots from authoritative strategic rows and persists only bounded inert text; generated values are never scripts, conditions, effects, or canonical truth. Runtime testimony is the one structured binding: each authoritative draft becomes a claim boundary with surrounding punctuation retained as text, and multiple drafts retain deterministic event-local order. The compiler rejects a testimony slot without its receive effect, the effect without exactly one slot, and attempts to place more than one testimony slot in a response. Prompts support yes_no, single, and multi choices and first_response, unanimous, majority, or all_respondents resolution. Choices may contain result_turns; these are appended to the durable transcript only after the prompt resolves and its effects succeed.

Conditions are a typed tree: always, all, any, not, and fact. Fact keys are allowlisted in FactKey; participant profession, organization, religion, familiarity, clothing, service role, location, time period, quest state, and flags are supported. New world facts require a Rust resolver change. Never put executable code, SQL, or client-trusted effects in content. Effects are likewise a closed enum. A client sends catalog revision and stable topic/choice IDs; the authoritative reducer resolves turns and effects from the embedded catalog.

Investigation dialogue uses generic facts and effects rather than per-case content IDs. A local-problem referral records the character-owned safe rumor receipt immediately when the tavern/overview conversation starts, without accepting a contract. Its observer-safe presentation is persisted once in the dialogue transcript immediately after the authored greeting. Referral turns name a known contact or describe them, give their occupation/relationship and expected location, and retain explicit uncertainty. Truthfulness, private motives, hidden causes, and undiscovered evidence never participate in topic eligibility. When the addressed NPC is the named contact, the referral switches to first-person wording and presents the testimony subject as an inline clickable phrase. A different same-named NPC is still explicitly disambiguated.

Generated return and exposure finales reuse compiled generic topics in both service and resident conversations. A topic is projected only when the server can pre-issue exactly one generated case/objective binding for the addressed NPC. Execution revalidates the public/canonical mapping, recipient, evidence or custody, session revision, and one-use binding before emitting a typed fact.

Run just dialogue-check before review. Use cargo run -p adventuresim-dialogue --bin dialogue-check -- explain <id> to inspect response priorities. Equal highest priorities at runtime are rejected as ambiguous instead of depending on file order.

Persistence and multiplayer

SpacetimeDB stores dialogue sessions, named participants, attributed events, open prompts, idempotent action receipts, per-character answers, and per-character topic knowledge. All raw dialogue rows are private; fail-closed gateway views are the only subscription surface, and the trusted web process additionally filters them to the selected character. Each player participant receives a projection row; nonparticipants receive none. The authored condition/effect catalog is never sent to browsers. Player organization facts come only from a current, locally recognized presented organization. The player profession is that organization's canonical starting_role.profession, rather than its organization ID; religion comes from authoritative profession-of-faith state. Reducers verify gateway authority, same-party membership, shared settlement, role cardinality, catalog and session revisions, topic eligibility, and stable choice IDs. Every NPC role is bound to a real persistent NPC, and every mutation revalidates each NPC's exact session location and current schedule. Selecting an NPC creates a fresh encounter so contextual and prior-interaction facts are reevaluated; old sessions remain history rather than an indefinitely reusable active view. Free-form local_chat_message remains an independent stream.

Organization business uses the dedicated compiled organization-representative conversation. Join, dues/reactivation, promotion, and presentation are closed effects with no authored or client-submitted organization ID. Strategic authority derives the institution only from the organization-bound representative NPC, verifies that the NPC occupies that institution's exact authored chapter location, then applies the existing membership authority. Membership state, promotion availability, dues, and current presentation are server-built dialogue facts. Before asking for confirmation, the representative names the organization and states the joining fee and admission requirements, the dues amount, interval, and current standing, or the current rank and next-rank requirements as applicable. Committed prompt answers are retry-safe when a response is lost; a new answer or an action receipt from a different prompt cannot mutate a closed prompt. The representative's greeting anchors a highlighted organization-business topic. Its follow-up links are selected from authoritative membership facts: nonmembers see joining, suspended members see dues only where dues exist, and current members can follow a gated chain through dues, promotion, and presentation without seeing actions unavailable in their current state.

The web conversation surface exposes topics only as highlighted phrases in NPC dialogue. Clicking one asks about that subject; there is no separate list of generic or undiscovered topics. While a prompt is open, the shared composer matches its choices, shows a unique prefix as grey inline completion, and lets Tab accept it. Multi-select answers use comma-separated choice labels. Other text continues through the independent free-form chat stream.

Developer mode and source editing

The hammer button immediately left of the character portrait toggles developer mode. It is off by default and persisted locally in the browser. Its content-editing consumers include dialogue and item definitions: authored NPC lines receive keyboard-accessible GitHub editor links, and expanding a concrete inventory row reveals an Edit YAML button. Repository and ref are centralized by the server (ADVENTURESIM_SOURCE_REF, default main); source paths and spans come from compilation, so writers never maintain line numbers. Unsupported or unsafe paths do not produce links. Extend developer mode only by querying the root data-developer-mode attribute; do not create independent toggles.

Schema changes are pre-launch and intentionally have no migration or legacy compatibility path. Recreate/reseed the development database and regenerate the SpacetimeDB client when deploying this schema.