{
  "tab": "world",
  "section": "quests",
  "title": "Quests (Advanced)",
  "summary": "Quests are pre-authored story objectives that start hidden and become visible only when a trigger fires `quest-init`. Every quest needs at least one trigger pointing to it - `quests` with no trigger are permanently unreachable.",
  "uiLocation": "World → Advanced → Quests",
  "uiSubtitle": "\"Quests that can be used by story starts or triggers\"",
  "editor": "JSON + ADD ITEM",
  "sizeLimits": [],
  "related": "triggers - every quest needs at least one `quest-init` trigger to become reachable",
  "wikiUrl": "/world/quests",
  "schema": {
    "_type": "record",
    "domain": "string",
    "codomain": {
      "_type": "union",
      "of": [
        {
          "_type": "intersection",
          "parts": [
            {
              "_type": "intersection",
              "parts": [
                {
                  "_type": "required",
                  "fields": {
                    "name": "string",
                    "questSource": "string",
                    "questStatement": "string",
                    "mainObjective": "string",
                    "completionCondition": "string"
                  }
                },
                {
                  "_type": "partial",
                  "fields": {
                    "conclusive": "boolean",
                    "questDesignBrief": "string"
                  }
                }
              ]
            },
            {
              "_type": "required",
              "fields": {
                "detailType": {
                  "_type": "literal",
                  "value": "basic"
                },
                "spatialRelationship": {
                  "_type": "union",
                  "of": [
                    {
                      "_type": "literal",
                      "value": "existingLocalArea"
                    },
                    {
                      "_type": "literal",
                      "value": "newLocalArea"
                    },
                    {
                      "_type": "literal",
                      "value": "nearbyNewLocation"
                    },
                    {
                      "_type": "literal",
                      "value": "distantNewLocation"
                    },
                    {
                      "_type": "literal",
                      "value": "existingLocationNewAreas"
                    }
                  ]
                }
              }
            }
          ]
        },
        {
          "_type": "intersection",
          "parts": [
            "(recursive)",
            {
              "_type": "required",
              "fields": {
                "detailType": {
                  "_type": "literal",
                  "value": "detailed"
                },
                "questLocation": "string"
              }
            }
          ]
        }
      ]
    }
  },
  "body": "## Example\r\n\r\n```json\r\n{\r\n  \"The Missing Documents\": {\r\n    \"name\": \"The Missing Documents\",\r\n    \"questType\": \"side\",\r\n    \"questSource\": \"Archivist Sera Vane\",\r\n    \"questStatement\": \"Side quest. Vane is a guild archivist who has identified a set of classified records being quietly prepared for destruction — documents that contradict the official history of a major land dispute. She cannot retrieve them herself without triggering a mandatory audit of her clearance. She needs an outside agent to enter the restricted archive during the next scheduled maintenance window and remove the records before they are incinerated.\",\r\n    \"mainObjective\": \"Recover the classified documents from the guild archive and deliver them to Vane's contact at the docks.\",\r\n    \"completionCondition\": \"The documents have been physically delivered to the dock contact and confirmed received.\",\r\n    \"detailType\": \"detailed\",\r\n    \"questLocation\": \"The Capital\",\r\n    \"questDesignBrief\": \"Vane is trusting the player with materials the guild would destroy. Frame this as a genuine trust exchange - she cannot do this herself. The documents matter; do not reduce the retrieval to a single skill check. Let the risk of exposure feel real.\"\r\n  },\r\n  \"The Final Reckoning\": {\r\n    \"name\": \"The Final Reckoning\",\r\n    \"questType\": \"main\",\r\n    \"questSource\": \"The Resistance\",\r\n    \"questStatement\": \"Main quest — final arc. The evidence gathered across the investigation is complete: financial records, witness depositions, and the incriminating vault documents. The Resistance has secured access to the tribunal hall and a sympathetic magistrate willing to hear a formal case. The player must present the full evidence cache at a scheduled tribunal session and hold the case together under cross-examination by council-appointed advocates.\",\r\n    \"mainObjective\": \"Bring the full evidence cache to the tribunal hall and formally present the case against the council.\",\r\n    \"completionCondition\": \"The evidence has been presented at the tribunal and the verdict delivered.\",\r\n    \"detailType\": \"detailed\",\r\n    \"questLocation\": \"The Tribunal\",\r\n    \"questDesignBrief\": \"The primary ending. The tribunal is the culmination of everything gathered across the scenario. Give it weight - let the verdict land with gravity. Do not compress the resolution.\"\r\n  }\r\n}\r\n```\r\n\r\n## Fields\r\n\r\n### completionCondition\r\n\r\n**Required.** Missing it causes a deeply nested Zod error like `quests.Name.1.0.0.completionCondition`.\r\n\r\nDetailed quests auto-generate a completion trigger from `completionCondition`. Basic quests need manual triggers. If `completionCondition` is empty, no auto-trigger is created for either type. (Documented in the upstream quests skill.)\r\n\r\n### detailType\r\n\r\nDetermines which location field is required and how the AI handles quest location:\r\n\r\n| `detailType` | Location source | What the AI does |\r\n|---|---|---|\r\n| `\"detailed\"` | `questLocation` (required) -- exact pre-built location name | Quest is pinned to that location; AI generates quest content there |\r\n| `\"basic\"` | `spatialRelationship` (required) -- spatial hint enum | AI generates a location on the fly based on the spatial hint |\r\n\r\n**Valid `detailType` values:** Only `\"basic\"` and `\"detailed\"`. `\"brief\"` is **not** valid — using it causes a Zod error listing every expected field. The error message is misleading (it looks like the whole quest schema is wrong) but the root cause is always the invalid `detailType` string.\r\n\r\n### questLocation\r\n\r\nRequired when `detailType` is `\"detailed\"`. **Must be a location name, not a region name.** Must exactly match a key in [`locations`](/world/locations) — a specific location display name like `\"Capital City Docks\"`, not its parent region name like `\"The Capital Region\"`. Region names cause \"Invalid questLocation\" warnings. Always use the specific location, not its parent region.\r\n\r\n### spatialRelationship\r\n\r\nRequired when `detailType` is `\"basic\"`. Codec enum with five accepted values:\r\n\r\n`existingLocalArea | newLocalArea | nearbyNewLocation | distantNewLocation | existingLocationNewAreas`\r\n\r\nWhat each tells the narrator to construct: `existingLocalArea` - the quest stays in the current area (no travel needed); `newLocalArea` - moving to a new part of the current location that didn't previously exist (e.g. a hidden basement); `nearbyNewLocation` - travel within the same region, framed as a short trip; `distantNewLocation` - a journey to a different region or far-off part of the world; `existingLocationNewAreas` - returning to a known location and discovering entirely new sections of it.\r\n\r\n> **For `basic` quests, use `existingLocalArea`** (or make the quest `detailType: \"detailed\"` with a `questLocation`). The location-generating values `nearbyNewLocation` and `distantNewLocation` resolve a new location *relative to the player's current position*, which can fail when the quest is accepted — the engine aborts with \"Failed to accept quest\". This bites starting quests (accepted at spawn, before any movement) and arc/chained quests offered before the player has travelled. Anchor the quest at the player's area and let outward travel emerge through play; reserve named destinations for `detailType: \"detailed\"` + `questLocation`.\r\n\r\n### questStatement\r\n\r\nIn practice carries most of the AI guidance load for individual quests — not just \"the situation that creates the quest\" but the full scene context: who is involved, what the player must do, where the encounter happens, and how success is judged. A well-written `questStatement` runs 100–500 characters. For authored quest chains, many authors open `questStatement` with a category label on its own line (`Premade Questline: Arc Name`, `AI Generated Quest`) before the narrative setup paragraph — this helps the AI understand the quest's origin and treat it accordingly.\r\n\r\n> **📋 Note:** `questStatement` and the global [`storySettings.questGenerationGuidance`](/world/storySettings) work as a general-to-specific pair. The global guidance carries world-wide quest tone so any quest feels like it belongs in the scenario; `questStatement` carries the mission-specific context so the objective lands with the right weight. `questDesignBrief` adds tone and feel guidance on top of that when the quest's emotional register is non-obvious from the other fields.\r\n\r\n### questDesignBrief\r\n\r\nOptional string — authoring notes about how the quest should feel and be run. Not player-visible. Include it for quests where the tone or pacing is non-obvious from the other fields alone.\r\n\r\n```json\r\n\"questDesignBrief\": \"Direct confrontation with the mastermind at their stronghold. They are willing to negotiate - this should feel like a revelation, not an automatic fight. No violence unless the player chooses it. Their account should answer questions and raise new ones.\"\r\n```\r\n\r\n### questType\r\n\r\nExtra-codec string. Category label used by the AI to frame the quest in the journal and in narration. Common values: `\"main\"`, `\"side\"`, `\"task\"`, `\"investigation\"`, `\"defense\"`, `\"infiltration\"`, `\"progression\"`. Not in the formal schema, but widely used and present in the editor UI. Include it on every quest.\r\n\r\n### npcs (extra-codec)\r\n\r\nArray of strings. NPC name keys central to this quest. Used by the engine to resolve \"NPC X is not referenced by any quest\" warnings in the editor. List every NPC the player will interact with during the quest. Strings must exactly match keys in [`npcs`](/world/npcs).\r\n\r\n### description (extra-codec)\r\n\r\nPlayer-facing summary shown in the quest log detail view. One to three sentences describing the situation and what the player needs to do. Different from `questDesignBrief` which is internal AI guidance only.\r\n\r\n### Validation gotchas\r\n\r\n> **⚠️ Warning:**\r\n> - `completionCondition` is required - omitting it causes a deeply nested Zod error.\r\n> - The correct trigger effect format for `quest-init` is `{ \"type\": \"quest-init\", \"operator\": \"set\", \"value\": \"Quest Name\" }` - `\"operator\": \"set\"` must be present.\r\n> - `questLocation` must be a **location** name (a key in `locations`), not a region name. Region names produce \"Invalid questLocation\" warnings.\r\n> - `detailType` accepts only `\"basic\"` and `\"detailed\"` - `\"brief\"` is invalid.\r\n\r\n## Quest lifecycle\r\n\r\n### Status flow\r\n\r\nHidden → (trigger fires `quest-init`) → Available → Accepted → **Phase 1: `goToLocation`** (move to the quest's region/location) → **Phase 2: `goToArea`** (move to the specific area within that location) → **Phase 3: `completeObjectives`** (player completes `mainObjective`) → Completed.\r\n\r\nFrom Available the player may also `reject` the offer (→ `rejected`) or the quest may `expire`. From Accepted the player may `abandon` (→ `abandoned`).\r\n\r\nValid quest statuses: `hidden`, `available`, `accepted`, `completed`, `abandoned`, `rejected`, `expired`.\r\n\r\n### Expiry conditions\r\n\r\nFrom `available` state, a quest expires when:\r\n- Expiry tick reached (3 ticks after offer)\r\n- Party leaves the location where the quest was offered\r\n- Quest giver dies, becomes incapacitated, or is no longer near the party\r\n\r\nAcceptance and rejection are immediate — there is no pending state between offer and decision.\r\n\r\n### Quest chains\r\n\r\nUse a `quests-completed` trigger condition to detect completion, then fire `quest-init` for the next quest.\r\n\r\n### acceptQuest UI prompt\r\n\r\nWhen a quest becomes available, the engine surfaces an accept prompt to the player as a UI element after the turn ends — accepted quests are tracked in the journal (top-left in the game). Use `quest-init` triggers with `story` conditions for quest discovery logic rather than relying on prose dialogue.\r\n\r\n### Trigger naming convention\r\n\r\nUse the pattern `{questId}_objective` or `{questId}_objective_N` (e.g. `the_missing_documents_objective`, `the_missing_documents_objective_2`) to name objective-phase triggers consistently. Triggers named with this pattern are automatically filtered out of the active pool while the quest is unaccepted or abandoned — they will not fire unless the quest is in an accepted state.\r\n\r\n## Authoring tips\r\n\r\n### Coverage requirement\r\n\r\n**Every quest must have at least one trigger with a `quest-init` effect pointing to it, or it will never become available to the player.** Quests without triggers remain permanently in the `Hidden` state — they exist in the data but can never be discovered or accepted. This is the most common cause of \"quests not showing up.\" Ensure 100% coverage: one trigger per quest at minimum.\r\n\r\n### Quality checklist\r\n\r\n- `questStatement` — reads as a briefing: who, what, where, why, and how success is judged. One sentence to a full paragraph depending on quest complexity. For authored quest chains, consider opening with a category label (`Premade Quest`, `AI Generated Quest`) on its own line before the narrative setup.\r\n- `mainObjective` — starts with an imperative verb. The engine parses this to evaluate completion.\r\n- `completionCondition` — write what \"done\" looks like in plain language. Be specific.\r\n- `detailType: \"detailed\"` + `questLocation` for pre-built locations; `detailType: \"basic\"` + `spatialRelationship` for AI-generated locations.\r\n\r\n### Discovery pattern (first quests at a location)\r\n\r\n1. Arrival trigger (`start_[location]`) sets scene and writes a boolean flag — no `quest-init`.\r\n2. Discovery trigger (`discover_[quest_slug]`) checks the flag + a `story` AI condition (\"has the player spoken with the quest-giver?\") → fires `quest-init`.\r\n3. The quest appears in the player's journal only after they have organically encountered the hook.\r\n\r\n### Chain pattern (multi-step storylines)\r\n\r\n1. Quest A surfaces via the two-step discovery pattern above.\r\n2. Quest B trigger has condition `quests-completed contains \"Quest A\"` — fires only after A is done.\r\n3. Quest C trigger chains from B in the same way.\r\n\r\nThis creates a natural investigation/escalation arc without the player being handed everything at once.\r\n\r\n### Encounter quests\r\n\r\nFor encounter/spawn-able enemies: create one quest per enemy faction with `questSource: \"Regional Danger\"`. These quests don't need complex objectives — they serve as AI context for encounter spawning.\r\n\r\n### NPC reference warnings\r\n\r\nThe editor shows \"NPC X is not referenced by any story start or quest\" for NPCs that have no structural linkage in the scenario. The quest schema has no built-in NPC linkage field — `questSource` is intentionally a plain string. To resolve these warnings, add every NPC to the `npcs` array of at least one quest. For enemy/encounter NPCs, create dedicated encounter quests (`questSource: \"Regional Danger\"`) with an `npcs` array listing the enemies. These quests give the AI context for encounter spawning and resolve the warnings entirely."
}