Quests
questsQuests are pre-authored story objectives that start hidden. Surface them deliberately -- a trigger firing quest-init behind a two-step discovery gate, or a story start's startingQuests; with light or no gating the engine can throw a quest at the player the moment they engage its quest giver.
"Durable goals that can be started by story starts, triggers, or completion effects."
- Editor location
- Files → World → Advanced → Quests
- Editor type
- Edit in Studio + View JSON
- In the world file
parties[""].quests- API endpoint
/sections/quests.json/sections/quests.md- Size limits
No character-length caps for this section.
Schema
{
"parties": {
"": {
"quests": {
"<key>": {
"name": "string",
"questSource": "string",
"questLocation": "string",
"detailType": "basic | detailed",
"status": "hidden | planned | available | expired | accepted | completed | abandoned | rejected",
"completionCondition": {
"type": "story",
"query": "string"
},
"onCompleteEffects": [
{
"type": "story",
"instruction": "string"
}
],
"questStatement": "string",
"mainObjective": "string",
"questDesignBrief": "string",
"objectives": {
"<key>": {
"id": "string",
"text": "string",
"status": "hidden | active | completed"
}
},
"activeObjectiveId": "string",
"nextStep": {
"text": "string",
"source": "objective | narrative-event"
},
"spatialRelationship": "existingLocalArea | newLocalArea | nearbyNewLocation | distantNewLocation | existingLocationNewAreas",
"conclusive": "boolean"
}
}
}
}
}Example
{
"parties": {
"": {
"quests": {
"The Missing Documents": {
"name": "The Missing Documents",
"questSource": "Archivist Sera Vane",
"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.",
"mainObjective": "Recover the classified documents from the guild archive and deliver them to Vane's contact at the docks.",
"completionCondition": {
"type": "story",
"query": "The documents have been physically delivered to the dock contact and confirmed received."
},
"detailType": "detailed",
"questLocation": "The Capital",
"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.",
"onCompleteEffects": [
{
"type": "quest-accept",
"operator": "set",
"value": "The Final Reckoning"
},
{
"type": "player-resource",
"target": "satisfyingPlayers",
"resource": "gold",
"operator": "add",
"value": 50
}
]
},
"The Final Reckoning": {
"name": "The Final Reckoning",
"questSource": "The Resistance",
"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.",
"mainObjective": "Bring the full evidence cache to the tribunal hall and formally present the case against the council.",
"completionCondition": {
"type": "story",
"query": "The evidence has been presented at the tribunal and the verdict delivered."
},
"detailType": "detailed",
"questLocation": "The Tribunal",
"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."
}
}
}
}
}Fields
name
The quest's display name. Unlike most keyed sections, quests do not have to match their key to name; matching them anyway keeps quests easy to find. Quest references (quest-init / quest-plan / quest-accept values, questId fields, startingQuests) resolve by quest key first, then by unique display name, so the key is the stable reference to author.
questSource
Where the quest originates (NPC name, location, object). Preserved unchanged into the running quest.
questStatement
One sentence describing the situation that creates the quest.
Format: [Subject] [situation that creates urgency or motivation]
mainObjective
What the player must accomplish -- shown in the quest log UI. Write it as a player action in imperative form.
Format: [Verb] the [target] or [Verb] [what] from/in/at [where]
completionCondition
Optional. How the engine detects that the quest is complete. Two shapes:
{ "type": "story", "query": "..." }-- the semantic text matched against the story to detect completion. Write the query as a natural-language description of what "done" looks like.{ "type": "narrative-event-completed", "eventId": "..." }-- the quest completes, if accepted, when that narrative event completes.
detailed quests auto-generate a completion trigger from a story condition (the query becomes the trigger's semantic condition, firing quest-progress); basic quests never do and need manual triggers. An empty query generates no trigger. Without a completionCondition, the quest completes only through explicit effects (quest-progress, quest-complete) from triggers, narrative events, or another quest's onCompleteEffects.
detailType
| Type | Use when | Location handling |
|---|---|---|
basic | Quest location should be AI-generated | Uses spatialRelationship to generate dynamically |
detailed | Quest has a specific predefined location | Uses exact questLocation from locations |
Use detailed for hand-crafted narrative quests. Use basic for procedural or emergent quests.
status
Required string naming the quest's position in the status flow: hidden, planned, available, accepted, completed, abandoned, rejected or expired.
An authored quest carries the status it enters play in. hidden is the usual choice, since quest-plan and quest-init move it on from there.
A quest entry without the field is rejected.
spatialRelationship
Required when detailType: "basic" -- defines where the AI generates the quest location.
| Value | Description |
|---|---|
existingLocalArea | Current location, existing area |
newLocalArea | Current location, new area created |
nearbyNewLocation | New location generated nearby |
distantNewLocation | New location generated far away |
existingLocationNewAreas | Existing location, new areas added |
questLocation
The quest's specific predefined location, a key from locations, used on detailed quests; the engine uses this exact location. A detailed quest with a blank questLocation is locationless: it skips the travel phases and starts straight in objective mode. On basic quests the engine fills the quest's location itself at runtime.
questDesignBrief
Internal design guidance for the AI: how the quest should feel and be run. It is read when the engine generates the quest's location details, and NPCs and locations the quest spawns carry it into their own detail generation. Not player-visible.
conclusive
Whether completing this quest concludes its parent story arc. Defaults to false.
objectives
Optional Record<string, object>. Every entry requires id (string), text (string) and status, where status is "hidden", "active" or "completed".
Objectives are shown to players. One with status "hidden" is left out of that list, and a listed objective reads as done when its own status is "completed" or the quest itself has completed.
The record is what activeObjectiveId and nextStep resolve against, and neither is kept without it.
activeObjectiveId
Optional string naming the objective that should be active when the quest begins. Leave it out for a quest that starts with no active authored objective.
It is matched against the keys of objectives, not the id inside each entry, and is dropped when it does not name one.
Warning: this is the record key of the objective, not the
idvalue stored inside the entry. Where the two differ, writing theidsilently drops the field and the quest begins with no active objective.
nextStep
Optional temporary next-step guidance shown to players. Both keys are required when it is present: text (string) is the player-facing instruction shown as the current next step, and source is "objective" or "narrative-event". Any other source is rejected.
"objective" ties the step to quest progress, and it is kept only when its text matches the text of one of the quest's objectives exactly. "narrative-event" marks it as temporary guidance owned by an active narrative event.
It is not fixed for the life of the quest: a trigger can replace or clear it with the quest-next-step-set and quest-next-step-clear effects (see Triggers).
onCompleteEffects
Trigger effects that fire once, the moment this quest completes. They take the same shape as a trigger's effects, so anything a trigger can do the quest can do on completion — open the next quest, start a narrative event, reveal an objective, grant a resource. This is the direct way to chain quests: the quest that finishes names its successor, with no separate trigger watching for it.
They fire last in the completion sequence, and the story phase sees them, so their consequences can be narrated. They are party-scoped: target: "satisfyingPlayers" resolves to nobody here. Effects beyond the per-trigger cap are dropped.
name
The quest's display name. Unlike most keyed sections, quests do not have to match their key to name; matching them anyway keeps quests easy to find. Quest references (quest-init / quest-plan / quest-accept values, questId fields, startingQuests) resolve by quest key first, then by unique display name, so the key is the stable reference to author.
completionCondition
Optional, and omitting it is a deliberate choice for trigger-driven quests. When present it is an object in one of two shapes:
{ "type": "story", "query": "..." }-- a story-completion check;queryis the situation that marks the quest done, in plain language ("The documents have been delivered to the dock contact."). This is the common form.{ "type": "narrative-event-completed", "eventId": "..." }-- the quest completes when the named narrative event completes. Use this to end a quest on the resolution of a multi-turn arc rather than a single detected moment.
Note: A plain string is also accepted and wrapped into
{ "type": "story", "query": <string> }on load. Author the object form directly.
Detailed quests auto-generate a hidden completion trigger named __generated_quest_progress__/<quest> from a story condition; don't give your own triggers names starting with that prefix. Basic quests and narrative-event-completed conditions build no trigger.
detailType
Determines which location field is required and how the AI handles quest location:
detailType | Location source | What the AI does |
|---|---|---|
"detailed" | questLocation -- exact pre-built location key; blank for a locationless quest | Quest is pinned to that location; AI generates quest content there |
"basic" | spatialRelationship (required) -- spatial hint enum | AI generates a location on the fly based on the spatial hint |
Valid detailType values: Only "basic" and "detailed". "brief" is not valid — using it causes a decoder 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.
questLocation
Used when detailType is "detailed". Must be a location, not a region. Must match a key in locations — a specific location 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.
spatialRelationship
Required when detailType is "basic". Codec enum with five accepted values:
existingLocalArea | newLocalArea | nearbyNewLocation | distantNewLocation | existingLocationNewAreas
What 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.
questStatement
The situation that creates the quest. A strong one reads as a briefing: who is involved, what the player must do, where it happens, and how success is judged.
Note:
questStatementand the globalstorySettings.questGenerationGuidancework as a general-to-specific pair. The global guidance carries world-wide quest tone so any quest feels like it belongs in the scenario;questStatementcarries the mission-specific context so the objective lands with the right weight.questDesignBriefadds tone and feel guidance on top of that when the quest's emotional register is non-obvious from the other fields.
questDesignBrief
Optional 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.
"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."Validation gotchas
Warning:
- The correct trigger effect format for
quest-initis{ "type": "quest-init", "operator": "set", "value": "Quest Name" }-"operator": "set"must be present.questLocationmust be a location key, not a region. Region names produce "Invalid questLocation" warnings.detailTypeaccepts only"basic"and"detailed"-"brief"is invalid.
Quest lifecycle
Status flow
Hidden → (optionally quest-plan) → Planned → (quest-init) → Available → Accepted → Completed. A story start's startingQuests makes hidden quests available at game start.
planned is invisible to players but reserves the storyline: the AI quest generator won't generate a duplicate of it. From Available the player may also reject the offer (→ rejected) or the offer may expire (→ expired). From Accepted the player may abandon it (→ abandoned). A hidden or planned quest can still accumulate objective progress silently.
Valid quest statuses: hidden, planned, available, accepted, completed, abandoned, rejected, expired.
Step phases
Once accepted, a quest moves through goToLocation (reach the quest's location), goToArea (reach a specific area in it), and completeObjectives, advancing automatically as the party arrives. Where it starts follows spatialRelationship: existingLocalArea and newLocalArea start at goToArea; existingLocationNewAreas starts at goToLocation and then goToArea; nearbyNewLocation and distantNewLocation start at goToLocation and skip straight to completeObjectives on arrival. A quest with a blank questLocation starts in completeObjectives.
Expiry conditions
From available state, a quest expires when:
- Its expiry tick is reached, if it has one
- Party leaves the location where the quest was offered
- Quest giver dies, becomes incapacitated, or is no longer near the party
A standing offer without an expiry stays valid indefinitely. Acceptance and rejection are immediate; there is no pending state between offer and decision.
Three ways to move a quest forward
Three trigger effects act on a quest by key, and they differ in how visible the result is:
| Effect | What it does |
|---|---|
quest-plan | Moves a hidden quest to planned: reserved, not shown. Use it to claim a storyline early so the quest generator does not invent something that overlaps it. |
quest-init | Moves a hidden or planned quest to available, which is what surfaces the accept prompt. It doesn't re-offer a quest in any other status. |
quest-accept | Moves a hidden, planned, or available authored quest straight to accepted. Re-accepting a concluded quest starts a fresh run with its objectives reset. |
More than one quest can be accepted at a time, so accepting a new one does not close out the others. A quest planned, offered, or accepted by an effect this turn suppresses the AI's generated quest for that turn.
Quest chains
Give the finishing quest an onCompleteEffects entry naming its successor — quest-accept to hand it straight over, quest-init to offer it. Nothing has to watch for the completion.
A quests-completed trigger condition still works when the follow-up depends on more than one quest being done, or on conditions the finishing quest cannot see.
acceptQuest UI prompt
When 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.
Trigger naming convention
Use 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. The name is a label only: gate the trigger on the quest's status yourself if it should not fire before the quest is accepted.
Authoring tips
Activation coverage
A quest starts hidden. The two deliberate activation paths are a trigger with a quest-init effect and a story start's startingQuests entry.
The working method is to gate quest-init behind a two-step discovery trigger -- an arrival trigger that sets a flag, then a story-condition trigger that fires quest-init once the player has actually met the hook (see the pattern on the Triggers page) -- so the quest surfaces naturally and reliably.
Light or missing gating does not keep a quest hidden: engaging the NPC the quest originates from can surface it immediately, landing it on the player with no narrative lead-in.
Quality checklist
questStatement— reads as a briefing: who, what, where, why, and how success is judged. One sentence to a full paragraph depending on quest complexity.mainObjective— starts with an imperative verb; it is the step players see when nothing more specific applies.completionCondition— write what "done" looks like in plain language. Be specific.detailType: "detailed"+questLocationfor pre-built locations;detailType: "basic"+spatialRelationshipfor AI-generated locations.
Discovery pattern (first quests at a location)
- Arrival trigger (
start_[location]) sets scene and writes a boolean flag — noquest-init. - Discovery trigger (
discover_[quest_slug]) checks the flag + astoryAI condition ("has the player spoken with the quest-giver?") → firesquest-init. - The quest appears in the player's journal only after they have organically encountered the hook.
Chain pattern (multi-step storylines)
- Quest A surfaces via the two-step discovery pattern above.
- Quest A's
onCompleteEffectsoffers or accepts Quest B, or a trigger withquests-completed contains "Quest A"does when B depends on more than A. - Quest C chains from B in the same way.
This creates a natural investigation/escalation arc without the player being handed everything at once.