{"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.","kind":"schema","uiLocation":"World → Advanced → Quests","uiSubtitle":"\"Quests that can be used by story starts or triggers\"","editor":"JSON + ADD ITEM","related":[{"section":"triggers","note":"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":{"_type":"union","of":[{"_type":"required","fields":{"type":{"_type":"literal","value":"story"},"query":"string"}},{"_type":"required","fields":{"type":{"_type":"literal","value":"narrative-event-completed"},"eventId":"string"}}]}}},{"_type":"partial","fields":{"conclusive":"boolean","questDesignBrief":"string","objectives":{"_type":"record","domain":"string","codomain":{"_type":"required","fields":{"id":"string","text":"string","status":{"_type":"union","of":[{"_type":"literal","value":"hidden"},{"_type":"literal","value":"active"},{"_type":"literal","value":"completed"}]}}}},"activeObjectiveId":"string","nextStep":{"_type":"required","fields":{"text":"string","source":{"_type":"union","of":[{"_type":"literal","value":"objective"},{"_type":"literal","value":"narrative-event"}]}}}}}]},{"_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"}}]}]}},"sizeLimits":[],"blocks":[{"type":"example","lang":"json","code":"{\n  \"The Missing Documents\": {\n    \"name\": \"The Missing Documents\",\n    \"questType\": \"side\",\n    \"questSource\": \"Archivist Sera Vane\",\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.\",\n    \"mainObjective\": \"Recover the classified documents from the guild archive and deliver them to Vane's contact at the docks.\",\n    \"completionCondition\": {\n      \"type\": \"story\",\n      \"query\": \"The documents have been physically delivered to the dock contact and confirmed received.\"\n    },\n    \"detailType\": \"detailed\",\n    \"questLocation\": \"The Capital\",\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.\"\n  },\n  \"The Final Reckoning\": {\n    \"name\": \"The Final Reckoning\",\n    \"questType\": \"main\",\n    \"questSource\": \"The Resistance\",\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.\",\n    \"mainObjective\": \"Bring the full evidence cache to the tribunal hall and formally present the case against the council.\",\n    \"completionCondition\": {\n      \"type\": \"story\",\n      \"query\": \"The evidence has been presented at the tribunal and the verdict delivered.\"\n    },\n    \"detailType\": \"detailed\",\n    \"questLocation\": \"The Tribunal\",\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.\"\n  }\n}"},{"type":"fields","fields":[{"name":"questType","tooltip":"Free-form string.","md":"Free-form string. A category label for the quest. There is no closed set — any string is accepted, so a world can coin labels that fit its own quest taxonomy (`\"bounty\"`, `\"diplomacy\"`, `\"exploration\"`, `\"crafting\"`, and so on). The categories seen most often are `\"acquire\"`, `\"rescue\"`, `\"combat\"`, `\"assassination\"`, `\"social\"`, and `\"investigate\"`; reach for one of those when it fits, and only invent a label when none does.\n\nThis field is **not in the codec schema** and is **not read by the engine**. It is a categorization and sorting label only and has no effect on narration, the journal, or any generation task. The editor and the validator accept it; author it for organization, not behavior — adding it changes nothing about how the quest is run."},{"name":"objectives","tooltip":"Optional map of sub-objectives, each with an id, text, and status.","md":"Optional map of sub-objectives for the quest, keyed by an id. Each entry is `{ \"id\", \"text\", \"status\" }`:\n\n- `id` -- the objective's identifier; **must be byte-identical to its outer map key** (same pattern as quests and triggers).\n- `text` -- the objective description shown to the player.\n- `status` -- one of `hidden`, `active`, or `completed`. Author the starting state (usually `hidden` for later steps, `active` for the first), then let triggers move objectives forward.\n\nObjectives are driven by triggers: the [`quest-objective-reveal`](/mechanics/triggers#reference) effect reveals a `hidden` objective, and [`quest-objective-complete`](/mechanics/triggers#reference) marks one `completed`. A quest with no `objectives` map behaves exactly as before -- objectives are an optional layer for quests that benefit from visible sub-steps.\n\n```json\n\"objectives\": {\n  \"reach_archive\": { \"id\": \"reach_archive\", \"text\": \"Reach the guild archive during the maintenance window.\", \"status\": \"active\" },\n  \"recover_records\": { \"id\": \"recover_records\", \"text\": \"Recover the classified records.\", \"status\": \"hidden\" }\n}\n```"},{"name":"activeObjectiveId","tooltip":"Optional. The id of the objective currently in focus.","md":"Optional string -- the `id` of the objective the player is currently working on. It points the engine (and the journal) at which objective is the current focus. Keep it pointing at an objective whose `status` is `active`; triggers that advance objectives typically update this alongside `quest-objective-complete`."},{"name":"nextStep","tooltip":"Optional next-step hint shown to the player, with its source.","md":"Optional object `{ \"text\", \"source\" }` giving the player a short \"what to do next\" hint for the quest.\n\n- `text` -- the hint shown to the player.\n- `source` -- where the hint comes from: `objective` (it reflects the active objective) or `narrative-event` (it reflects an active narrative event).\n\nYou can author an initial `nextStep`, but it is normally managed at runtime by the [`quest-next-step-set`](/mechanics/triggers#reference) and `quest-next-step-clear` trigger effects as the quest progresses. A world can also set a party-wide hint (independent of any quest) with the `party-next-step-set` / `party-next-step-clear` effects."}]},{"type":"prose","md":"### name\n\nDisplay name of the quest, and **must be byte-identical to the outer map key** — the engine matches entries by key, so the key and `name` have to be exactly equal (same case, same spaces). A mismatch is a validation error. Build the map as `{ quest[\"name\"]: quest }`.\n\n### completionCondition\n\n**Required.** Missing it causes a deeply nested Zod error like `quests.Name.1.0.0.completionCondition`.\n\nIt is an object in one of two shapes:\n\n- `{ \"type\": \"story\", \"query\": \"...\" }` -- a story-completion check; `query` is the situation that marks the quest done, in plain language (`\"The documents have been delivered to the dock contact.\"`). This is the common form.\n- `{ \"type\": \"narrative-event-completed\", \"eventId\": \"...\" }` -- the quest completes when the named [narrative event](/world/narrativeEvents) completes. Use this to end a quest on the resolution of a multi-turn arc rather than a single detected moment.\n\n> **📋 Note:** Earlier versions used a plain **string** here. That form is legacy: on load it is wrapped into `{ \"type\": \"story\", \"query\": <string> }`. Author the object form directly.\n\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.\n\n### detailType\n\nDetermines which location field is required and how the AI handles quest location:\n\n| `detailType` | Location source | What the AI does |\n|---|---|---|\n| `\"detailed\"` | `questLocation` (required) -- exact pre-built location name | Quest is pinned to that location; AI generates quest content there |\n| `\"basic\"` | `spatialRelationship` (required) -- spatial hint enum | AI generates a location on the fly based on the spatial hint |\n\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.\n\n### questLocation\n\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.\n\n### spatialRelationship\n\nRequired when `detailType` is `\"basic\"`. Codec enum with five accepted values:\n\n`existingLocalArea | newLocalArea | nearbyNewLocation | distantNewLocation | existingLocationNewAreas`\n\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.\n\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`.\n\n### questStatement\n\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.\n\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.\n\n### questDesignBrief\n\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.\n\n```json\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.\"\n```\n\n### Validation gotchas\n\n> **⚠️ Warning:**\n> - `completionCondition` is required - omitting it causes a deeply nested Zod error.\n> - The correct trigger effect format for `quest-init` is `{ \"type\": \"quest-init\", \"operator\": \"set\", \"value\": \"Quest Name\" }` - `\"operator\": \"set\"` must be present.\n> - `questLocation` must be a **location** name (a key in `locations`), not a region name. Region names produce \"Invalid questLocation\" warnings.\n> - `detailType` accepts only `\"basic\"` and `\"detailed\"` - `\"brief\"` is invalid."},{"type":"prose","md":"## Objectives and the next-step hint\n\nBeyond the single `mainObjective`, a quest can carry a map of **objectives** -- ordered sub-steps the player works through -- and a **next-step hint** that tells them what to do right now. Both are optional; a quest with neither behaves exactly as a plain quest.\n\n### Objectives\n\nEach objective has an `id`, player-facing `text`, and a `status` of `hidden`, `active`, or `completed`. Author the opening state (typically the first objective `active`, later ones `hidden`) and then drive them with triggers:\n\n| Effect | What it does |\n|---|---|\n| `quest-objective-reveal` | reveals a `hidden` objective to the player |\n| `quest-objective-complete` | marks an objective `completed` |\n| `quest-complete` | marks the whole quest complete |\n\nPoint `activeObjectiveId` at whichever objective is the current focus. A typical flow: complete objective A with `quest-objective-complete`, reveal objective B with `quest-objective-reveal`, and update `activeObjectiveId` to B -- all in the same trigger.\n\n### Next-step hint\n\n`nextStep` is a short `{ text, source }` prompt telling the player what to do next. Set it with `quest-next-step-set` and clear it with `quest-next-step-clear`; `source` records whether the hint reflects the active `objective` or an active `narrative-event`. There is also a party-wide hint, independent of any quest, managed with `party-next-step-set` / `party-next-step-clear`.\n\nAll of these are ordinary [trigger effects](/mechanics/triggers#reference) -- objectives and next steps are authored as data on the quest, then advanced by the same trigger system that surfaces the quest in the first place."},{"type":"prose","md":"## Quest lifecycle\n\n### Status flow\n\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.\n\nFrom Available the player may also `reject` the offer (→ `rejected`) or the quest may `expire`. From Accepted the player may `abandon` (→ `abandoned`).\n\nValid quest statuses: `hidden`, `available`, `accepted`, `completed`, `abandoned`, `rejected`, `expired`.\n\n### Expiry conditions\n\nFrom `available` state, a quest expires when:\n- Expiry tick reached (3 ticks after offer)\n- Party leaves the location where the quest was offered\n- Quest giver dies, becomes incapacitated, or is no longer near the party\n\nAcceptance and rejection are immediate — there is no pending state between offer and decision.\n\n### Quest chains\n\nUse a `quests-completed` trigger condition to detect completion, then fire `quest-init` for the next quest.\n\n### acceptQuest UI prompt\n\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.\n\n### Trigger naming convention\n\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."},{"type":"prose","md":"## Authoring tips\n\n### Coverage requirement\n\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.\n\n### Quality checklist\n\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.\n- `mainObjective` — starts with an imperative verb. The engine parses this to evaluate completion.\n- `completionCondition` — write what \"done\" looks like in plain language. Be specific.\n- `detailType: \"detailed\"` + `questLocation` for pre-built locations; `detailType: \"basic\"` + `spatialRelationship` for AI-generated locations.\n\n### Discovery pattern (first quests at a location)\n\n1. Arrival trigger (`start_[location]`) sets scene and writes a boolean flag — no `quest-init`.\n2. Discovery trigger (`discover_[quest_slug]`) checks the flag + a `story` AI condition (\"has the player spoken with the quest-giver?\") → fires `quest-init`.\n3. The quest appears in the player's journal only after they have organically encountered the hook.\n\n### Chain pattern (multi-step storylines)\n\n1. Quest A surfaces via the two-step discovery pattern above.\n2. Quest B trigger has condition `quests-completed contains \"Quest A\"` — fires only after A is done.\n3. Quest C trigger chains from B in the same way.\n\nThis creates a natural investigation/escalation arc without the player being handed everything at once.\n\n### Encounter quests\n\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.\n\n### NPC reference warnings\n\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."}],"body":"## Example\n\n```json\n{\n  \"The Missing Documents\": {\n    \"name\": \"The Missing Documents\",\n    \"questType\": \"side\",\n    \"questSource\": \"Archivist Sera Vane\",\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.\",\n    \"mainObjective\": \"Recover the classified documents from the guild archive and deliver them to Vane's contact at the docks.\",\n    \"completionCondition\": {\n      \"type\": \"story\",\n      \"query\": \"The documents have been physically delivered to the dock contact and confirmed received.\"\n    },\n    \"detailType\": \"detailed\",\n    \"questLocation\": \"The Capital\",\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.\"\n  },\n  \"The Final Reckoning\": {\n    \"name\": \"The Final Reckoning\",\n    \"questType\": \"main\",\n    \"questSource\": \"The Resistance\",\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.\",\n    \"mainObjective\": \"Bring the full evidence cache to the tribunal hall and formally present the case against the council.\",\n    \"completionCondition\": {\n      \"type\": \"story\",\n      \"query\": \"The evidence has been presented at the tribunal and the verdict delivered.\"\n    },\n    \"detailType\": \"detailed\",\n    \"questLocation\": \"The Tribunal\",\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.\"\n  }\n}\n```\n\n## Fields\n\n### questType\n\nFree-form string. A category label for the quest. There is no closed set — any string is accepted, so a world can coin labels that fit its own quest taxonomy (`\"bounty\"`, `\"diplomacy\"`, `\"exploration\"`, `\"crafting\"`, and so on). The categories seen most often are `\"acquire\"`, `\"rescue\"`, `\"combat\"`, `\"assassination\"`, `\"social\"`, and `\"investigate\"`; reach for one of those when it fits, and only invent a label when none does.\n\nThis field is **not in the codec schema** and is **not read by the engine**. It is a categorization and sorting label only and has no effect on narration, the journal, or any generation task. The editor and the validator accept it; author it for organization, not behavior — adding it changes nothing about how the quest is run.\n\n### objectives\n\nOptional map of sub-objectives for the quest, keyed by an id. Each entry is `{ \"id\", \"text\", \"status\" }`:\n\n- `id` -- the objective's identifier; **must be byte-identical to its outer map key** (same pattern as quests and triggers).\n- `text` -- the objective description shown to the player.\n- `status` -- one of `hidden`, `active`, or `completed`. Author the starting state (usually `hidden` for later steps, `active` for the first), then let triggers move objectives forward.\n\nObjectives are driven by triggers: the [`quest-objective-reveal`](/mechanics/triggers#reference) effect reveals a `hidden` objective, and [`quest-objective-complete`](/mechanics/triggers#reference) marks one `completed`. A quest with no `objectives` map behaves exactly as before -- objectives are an optional layer for quests that benefit from visible sub-steps.\n\n```json\n\"objectives\": {\n  \"reach_archive\": { \"id\": \"reach_archive\", \"text\": \"Reach the guild archive during the maintenance window.\", \"status\": \"active\" },\n  \"recover_records\": { \"id\": \"recover_records\", \"text\": \"Recover the classified records.\", \"status\": \"hidden\" }\n}\n```\n\n### activeObjectiveId\n\nOptional string -- the `id` of the objective the player is currently working on. It points the engine (and the journal) at which objective is the current focus. Keep it pointing at an objective whose `status` is `active`; triggers that advance objectives typically update this alongside `quest-objective-complete`.\n\n### nextStep\n\nOptional object `{ \"text\", \"source\" }` giving the player a short \"what to do next\" hint for the quest.\n\n- `text` -- the hint shown to the player.\n- `source` -- where the hint comes from: `objective` (it reflects the active objective) or `narrative-event` (it reflects an active narrative event).\n\nYou can author an initial `nextStep`, but it is normally managed at runtime by the [`quest-next-step-set`](/mechanics/triggers#reference) and `quest-next-step-clear` trigger effects as the quest progresses. A world can also set a party-wide hint (independent of any quest) with the `party-next-step-set` / `party-next-step-clear` effects.\n\n### name\n\nDisplay name of the quest, and **must be byte-identical to the outer map key** — the engine matches entries by key, so the key and `name` have to be exactly equal (same case, same spaces). A mismatch is a validation error. Build the map as `{ quest[\"name\"]: quest }`.\n\n### completionCondition\n\n**Required.** Missing it causes a deeply nested Zod error like `quests.Name.1.0.0.completionCondition`.\n\nIt is an object in one of two shapes:\n\n- `{ \"type\": \"story\", \"query\": \"...\" }` -- a story-completion check; `query` is the situation that marks the quest done, in plain language (`\"The documents have been delivered to the dock contact.\"`). This is the common form.\n- `{ \"type\": \"narrative-event-completed\", \"eventId\": \"...\" }` -- the quest completes when the named [narrative event](/world/narrativeEvents) completes. Use this to end a quest on the resolution of a multi-turn arc rather than a single detected moment.\n\n> **📋 Note:** Earlier versions used a plain **string** here. That form is legacy: on load it is wrapped into `{ \"type\": \"story\", \"query\": <string> }`. Author the object form directly.\n\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.\n\n### detailType\n\nDetermines which location field is required and how the AI handles quest location:\n\n| `detailType` | Location source | What the AI does |\n|---|---|---|\n| `\"detailed\"` | `questLocation` (required) -- exact pre-built location name | Quest is pinned to that location; AI generates quest content there |\n| `\"basic\"` | `spatialRelationship` (required) -- spatial hint enum | AI generates a location on the fly based on the spatial hint |\n\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.\n\n### questLocation\n\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.\n\n### spatialRelationship\n\nRequired when `detailType` is `\"basic\"`. Codec enum with five accepted values:\n\n`existingLocalArea | newLocalArea | nearbyNewLocation | distantNewLocation | existingLocationNewAreas`\n\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.\n\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`.\n\n### questStatement\n\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.\n\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.\n\n### questDesignBrief\n\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.\n\n```json\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.\"\n```\n\n### Validation gotchas\n\n> **⚠️ Warning:**\n> - `completionCondition` is required - omitting it causes a deeply nested Zod error.\n> - The correct trigger effect format for `quest-init` is `{ \"type\": \"quest-init\", \"operator\": \"set\", \"value\": \"Quest Name\" }` - `\"operator\": \"set\"` must be present.\n> - `questLocation` must be a **location** name (a key in `locations`), not a region name. Region names produce \"Invalid questLocation\" warnings.\n> - `detailType` accepts only `\"basic\"` and `\"detailed\"` - `\"brief\"` is invalid.\n\n## Objectives and the next-step hint\n\nBeyond the single `mainObjective`, a quest can carry a map of **objectives** -- ordered sub-steps the player works through -- and a **next-step hint** that tells them what to do right now. Both are optional; a quest with neither behaves exactly as a plain quest.\n\n### Objectives\n\nEach objective has an `id`, player-facing `text`, and a `status` of `hidden`, `active`, or `completed`. Author the opening state (typically the first objective `active`, later ones `hidden`) and then drive them with triggers:\n\n| Effect | What it does |\n|---|---|\n| `quest-objective-reveal` | reveals a `hidden` objective to the player |\n| `quest-objective-complete` | marks an objective `completed` |\n| `quest-complete` | marks the whole quest complete |\n\nPoint `activeObjectiveId` at whichever objective is the current focus. A typical flow: complete objective A with `quest-objective-complete`, reveal objective B with `quest-objective-reveal`, and update `activeObjectiveId` to B -- all in the same trigger.\n\n### Next-step hint\n\n`nextStep` is a short `{ text, source }` prompt telling the player what to do next. Set it with `quest-next-step-set` and clear it with `quest-next-step-clear`; `source` records whether the hint reflects the active `objective` or an active `narrative-event`. There is also a party-wide hint, independent of any quest, managed with `party-next-step-set` / `party-next-step-clear`.\n\nAll of these are ordinary [trigger effects](/mechanics/triggers#reference) -- objectives and next steps are authored as data on the quest, then advanced by the same trigger system that surfaces the quest in the first place.\n\n## Quest lifecycle\n\n### Status flow\n\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.\n\nFrom Available the player may also `reject` the offer (→ `rejected`) or the quest may `expire`. From Accepted the player may `abandon` (→ `abandoned`).\n\nValid quest statuses: `hidden`, `available`, `accepted`, `completed`, `abandoned`, `rejected`, `expired`.\n\n### Expiry conditions\n\nFrom `available` state, a quest expires when:\n- Expiry tick reached (3 ticks after offer)\n- Party leaves the location where the quest was offered\n- Quest giver dies, becomes incapacitated, or is no longer near the party\n\nAcceptance and rejection are immediate — there is no pending state between offer and decision.\n\n### Quest chains\n\nUse a `quests-completed` trigger condition to detect completion, then fire `quest-init` for the next quest.\n\n### acceptQuest UI prompt\n\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.\n\n### Trigger naming convention\n\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.\n\n## Authoring tips\n\n### Coverage requirement\n\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.\n\n### Quality checklist\n\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.\n- `mainObjective` — starts with an imperative verb. The engine parses this to evaluate completion.\n- `completionCondition` — write what \"done\" looks like in plain language. Be specific.\n- `detailType: \"detailed\"` + `questLocation` for pre-built locations; `detailType: \"basic\"` + `spatialRelationship` for AI-generated locations.\n\n### Discovery pattern (first quests at a location)\n\n1. Arrival trigger (`start_[location]`) sets scene and writes a boolean flag — no `quest-init`.\n2. Discovery trigger (`discover_[quest_slug]`) checks the flag + a `story` AI condition (\"has the player spoken with the quest-giver?\") → fires `quest-init`.\n3. The quest appears in the player's journal only after they have organically encountered the hook.\n\n### Chain pattern (multi-step storylines)\n\n1. Quest A surfaces via the two-step discovery pattern above.\n2. Quest B trigger has condition `quests-completed contains \"Quest A\"` — fires only after A is done.\n3. Quest C trigger chains from B in the same way.\n\nThis creates a natural investigation/escalation arc without the player being handed everything at once.\n\n### Encounter quests\n\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.\n\n### NPC reference warnings\n\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."}