Quests Reference

Complete documentation for tabs/quests.json.

QuestDefinition Schema

type QuestDefinition = BasicQuestDefinition | DetailedQuestDefinition

interface QuestBaseDefinition {
  name: string                    // ✅ Quest display name, must match object key
  questSource: string             // ✅ Where quest originates (NPC, location, etc.)
  questStatement: string          // ✅ One-sentence quest description
  mainObjective: string           // ✅ Primary goal text shown in quest log
  completionCondition?: QuestCompletionCondition  // ✅ How quest completion is detected (see below); omit for quests completed only by explicit effects
  status?: 'hidden' | 'planned' | 'available' | 'accepted'  // ⚠️ Starting status (default: hidden); see Starting Status below
  questGiverNPC?: string          // ❌ Stripped on save; the engine assigns givers to quests it generates or details. Name the giver in questSource and questDesignBrief
  questDesignBrief?: string       // ✅ Free-form design guidance for AI quest generation
  conclusive?: boolean            // ⚠️ Whether completing this quest concludes its parent arc (default: false)
  objectives?: Record<string, QuestObjective>  // ✅ Authored objective ladder, keyed by objective id
  activeObjectiveId?: string      // ✅ Currently highlighted objective (normally managed by trigger effects)
  nextStep?: QuestNextStep        // ✅ Short guidance shown on the quest UI (normally managed by trigger effects)
  onCompleteEffects?: Effect[]    // ✅ Applied once when the quest completes; same effect objects as triggers (see below)
}

type QuestCompletionCondition =
  | { type: 'story'; query: string }                        // ✅ Semantic query matched against the story
  | { type: 'narrative-event-completed'; eventId: string }  // ✅ Completes when the referenced narrative event completes

interface QuestObjective {
  id: string                      // ✅ Must match the objectives record key
  text: string                    // ✅ Imperative objective text shown to the player
  status: 'hidden' | 'active' | 'completed'  // ✅ Typically authored 'hidden' and revealed via triggers
}

interface QuestNextStep {
  text: string                    // ✅ Short imperative guidance
  source: 'objective' | 'narrative-event'  // ✅ What is driving this next step
}

interface BasicQuestDefinition extends QuestBaseDefinition {
  detailType: 'basic'             // ✅ AI generates location details
  spatialRelationship: SpatialRelationship  // ✅ Where to generate location
}

interface DetailedQuestDefinition extends QuestBaseDefinition {
  detailType: 'detailed'          // ✅ Uses predefined location
  questLocation?: string          // ⚠️ Key from locations.json; engine fills '' when omitted (locationless quest, see Quest Step Phases)
}

Legend

  • ✅ Predefine-able: Can be set in config, preserved via spread
  • ⚠️ Calculated default: Has fallback logic if not predefined
  • ❌ Always overwritten: Set by initialization regardless of what exists in config

Completion Conditions

completionCondition is an optional object with one of two forms:

Form Meaning
{ "type": "story", "query": "..." } The query is a natural-language description of what “done” looks like, matched semantically against the story. Drives the auto-generated completion trigger (see below).
{ "type": "narrative-event-completed", "eventId": "..." } The quest completes (if accepted) when the referenced narrative event reaches completed status. eventId references the narrativeEvents world section (see the narrative-events skill).

Legacy plain-string completion conditions are auto-converted to the story form ({ "type": "story", "query": "<the string>" }).

completionCondition is optional, and omitting it is a first-class choice for trigger-driven quests. A quest without one completes only through explicit effects: quest-progress reaching completion, or quest-complete, fired by triggers, narrative events, or another quest’s onCompleteEffects. There is no AI-judged fallback.

Starting Status

status is the quest’s starting status, authored like any other starting value (default hidden). Play moves it on from there. An authored world can begin a quest only at one of these four values; the later lifecycle statuses are reached through play:

Value At world start
hidden Invisible; the default
planned Invisible, but reserves its storyline (see Planned Quests below)
available The quest is on offer at turn 0
accepted The quest is fully accepted at turn 0: target location revealed, quest phase set, party guidance active

When several quests start accepted, all stay accepted and the last one becomes the active quest.

Worlds written before this field was renamed may still carry initialStatus. That key is no longer part of the schema and is not read, so move its value to status.

Planned Quests and Duplicate Avoidance

planned sits between hidden and available: the quest is invisible to players, but it reserves its storyline. The AI quest generator treats a planned quest as “do not duplicate”, and if the planned quest has an arc, no new quest is generated for that arc while it stands. Standing available offers discourage duplicate generation the same way.

To make a future quest shape and deduplicate AI generation, author it planned (via status or a quest-plan trigger effect); hidden keeps a quest purely invisible, without feeding duplicate avoidance.

A planned or hidden quest can still accumulate objective progress silently, with no player-facing quest toasts.

Quest Runtime Schema

interface Quest {
  // === FROM DEFINITION (preserved via spread) ===
  name: string                    // ✅ From definition
  questSource: string             // ✅ From definition
  questStatement?: string         // ✅ From definition
  mainObjective?: string          // ✅ From definition
  completionCondition?: QuestCompletionCondition  // ✅ From definition (legacy strings auto-converted to story form); stays absent when omitted
  questGiverNPC?: string          // ❌ Engine-assigned for generated quests; an authored value is stripped on save
  spatialRelationship?: SpatialRelationship  // ✅ From definition (basic only)
  questDesignBrief?: string       // ✅ From definition
  conclusive?: boolean            // ⚠️ From definition (default: false)
  objectives?: Record<string, QuestObjective>  // ✅ From definition; advanced only via trigger effects
  activeObjectiveId?: string      // ✅ From definition; dropped at load if it matches no objective
  nextStep?: QuestNextStep        // ✅ From definition; normally managed by objective/next-step trigger effects
  onCompleteEffects?: Effect[]    // ✅ From definition; applied once when the quest completes

  // === CONDITIONALLY SET ===
  questLocation: string           // ⚠️ From definition if provided, else '' (locationless quest)

  // === ALWAYS OVERWRITTEN ===
  id: string                      // ❌ Always generated UUID
  definitionKey?: string          // ❌ Engine-assigned link back to the authored quest file
  creationTick: number            // ❌ Always current tick
  detailType: 'basic' | 'detailed'  // ❌ Always set from definition
  status: QuestStatus             // ⚠️ Starts from the authored status (default 'hidden'); play moves it on from there
  contentOrigin: 'authored' | 'generated'  // ❌ 'authored' for world-defined quests, 'generated' for engine-created quests

  // === ARC FIELDS (engine-set on generated quests; stripped from authored files) ===
  arcId?: string                  // ❌ Engine-set link to the quest's arc (generated quests only); stripped on save
  arcEscalationAtCreation?: number  // ❌ Engine snapshot of arc escalation at quest creation; stripped on save

  // === RUNTIME STATE (set during gameplay) ===
  questType?: 'acquire' | 'rescue' | 'combat' | 'assassination' | 'social' | 'investigate'  // Quest category (never read by engine)
  detectionTick?: number          // Set when quest becomes available
  acceptedTick?: number           // Set when player accepts
  expiryTick?: number             // Set if quest can expire
  completedTick?: number          // Set when quest completed
  abandonedTick?: number          // Set when quest abandoned
  rejectedTick?: number           // Set when player rejects
  startingArea?: string           // Generated for basic quests
  connectingAreaName?: string     // Area connecting to quest location
  questAreas?: string[]           // Areas created for the quest
  questStepPhase?: 'goToLocation' | 'goToArea' | 'completeObjectives'
  hasVisitedLocation?: boolean    // Tracks if party reached quest location
  hasVisitedStartingArea?: boolean  // Tracks if party reached starting area
  objectiveCompleted?: boolean
  offeredAtLocation?: string      // Location where the quest was offered (engine-set)
  questGiverNPCKey?: string       // Runtime NPC key reference (engine-set)
  arcQuestOrdinal?: number        // Quest's position within its arc (engine-set)
}

type QuestStatus = 'hidden' | 'planned' | 'available' | 'expired' | 'accepted' | 'completed' | 'abandoned' | 'rejected'

type SpatialRelationship =
  | 'existingLocalArea'
  | 'newLocalArea'
  | 'nearbyNewLocation'
  | 'distantNewLocation'
  | 'existingLocationNewAreas'

contentOrigin is a pure label shown on the quest card (“Authored Quest” vs “Generated Quest”). It gates no engine behavior and is always stamped by the engine: quests defined in the world config get 'authored', quests the engine generates during play get 'generated'.

definitionKey is stamped by the engine on runtime quests to link them back to the authored quest file entry. The quest FILE ID (the record key in tabs/quests.json) is the stable reference to author: quest-init / quest-plan / quest-accept effect values, questId fields on quest-* effects and the quest-status condition, and storyStarts.startingQuests all resolve by runtime id first, then file id, then unique display name.

Quest Lifecycle

Definition -> hidden -> planned -> available -> accepted -> completed
                                       |  \         |
                                       |   rejected |
                                       |       abandoned
                                       |
                                    expired
Status Description How to Reach
hidden Quest exists but invisible to player; can accumulate objective progress silently Default at creation
planned Invisible to players, but reserves its storyline against duplicate AI generation Authored status: 'planned' or a quest-plan effect
available Quest can be discovered/offered Authored status: 'available', story start, or a quest-init effect
accepted Player actively pursuing Player accepts the offer, authored status: 'accepted', or a quest-accept effect
rejected Player declined the offer Player rejects the offer
completed Objectives achieved Completion trigger fires, quest progress reaches completion, or the referenced narrative event completes
abandoned Player gave up Player abandons quest
expired Time limit exceeded, party left, or giver gone See expiry conditions below

Status Transitions via Effects

  • quest-plan moves hidden to planned, and nothing else: quests in any other status keep their status.
  • quest-init moves hidden or planned to available. Quests in any other status keep their status. Worlds that relied on quest-init re-offering or resurrecting expired or completed quests must switch to quest-accept or restructure.
  • quest-accept (predefined, authored quests only) moves hidden, planned, or available to accepted.

Re-accepting a concluded quest (completed, abandoned, rejected, expired) is a fresh run: objectives reset to hidden and the terminal history is cleared. Progress made before first acceptance, while the quest was hidden, planned, or available, is kept.

A quest a trigger or effect chain just planned, offered, or accepted this turn suppresses the AI’s generated quest for that turn: authored quest chains win over ambient generation.

Quest Acceptance

When an available quest is offered, the player either accepts or rejects it as a single step. Acceptance and rejection are immediate (no separate “pending” state).

Expiry Conditions

An available quest with an expiry lapses to expired when any of these become true:

  • The expiry tick is reached
  • The party leaves the location where the quest was offered
  • The quest giver dies, becomes incapacitated, or is no longer near the party

A standing offer without an expiry stays valid indefinitely and keeps discouraging duplicate AI generation.

Quest Step Phases

Once accepted, quests track progress through phases:

Phase Description
goToLocation Player must reach the quest’s location (any area within it)
goToArea Player must reach a specific area within the location
completeObjectives Player is at the correct location/area, working on objectives

Phase auto-advances when the player reaches the required location or area.

Tracking fields:

  • hasVisitedLocation: Boolean tracking if party ever reached the quest location
  • hasVisitedStartingArea: Boolean tracking if party ever reached the starting area within that location

Phase initialization logic:

  • Local quests (existingLocalArea, newLocalArea): Start at goToArea phase (player already at location)
  • Remote quests with area navigation (existingLocationNewAreas): Start at goToLocation, then transition to goToArea once at location
  • Other remote quests (nearbyNewLocation, distantNewLocation): Start at goToLocation, then skip directly to completeObjectives once at location

Note: Quest phases and tracking fields are runtime-only. Do not manually set questStepPhase, hasVisitedLocation, or hasVisitedStartingArea in quest definitions - the game engine initializes these automatically based on the quest’s spatialRelationship.

An authored basic quest with a blank questLocation counts as already at the location: it skips the travel phases and advances to completeObjectives immediately.

A detailed quest without a questLocation behaves the same way: it skips travel, starts directly in objective mode (completeObjectives), and shows its mainObjective as the current step; no location is revealed on accept. spatialRelationship semantics are unchanged and apply to basic quests only.

Travel Difficulty and Movement Locks

Travel to a quest location is simple by default. The narrator treats a move as impossible only when the destination’s text explicitly establishes a lock: a boss guarding it, sealed access, a required access item, or authority clearance. Otherwise a move costs at most a skill check (when the party is restrained, in combat, or the destination is not yet established). A quest does not gate travel by itself; to make a quest location gated, state the lock in the location or area text and echo it in questDesignBrief.

Objectives and Next Steps

objectives is an authored objective ladder. Objectives advance exclusively through trigger effects — no AI decides when an objective is revealed or completed:

  • quest-objective-reveal sets an objective to active, makes it the quest’s active objective, and sets the quest’s next step to the objective’s text.
  • quest-objective-complete marks the objective completed and advances the next step to the next active objective (or clears it if none remains).

Revealing or completing an objective produces a player-visible status update when the quest is visible.

Typical authoring pattern: author objectives with status: 'hidden' and reveal them via triggers as the story progresses. An objective authored with status: 'active' is live from the start.

activeObjectiveId points at the currently highlighted objective. It is normally managed by the reveal/complete effects; set it yourself only to point at an objective you authored as initially active. If it matches no objective, it is dropped at load.

nextStep is short imperative guidance shown on the quest UI and injected into story and NPC prompts (“Next Step: …”). It is normally managed by the objective effects and the quest-next-step-set / quest-next-step-clear trigger effects. Next steps with source: 'narrative-event' are auto-cleared when the driving narrative event completes or stops. An authored nextStep with source: 'objective' whose text matches no objective is dropped at load.

Player-facing next-step display resolution order: quest nextStep text → current objective text → travel-phase fallback → mainObjective.

onCompleteEffects

onCompleteEffects on a quest definition is an array of effects using the exact same effect vocabulary as triggers, including the npc-* and player-* effects. Full effect semantics live in the triggers reference (see the triggers skill).

  • They fire once, last in the completion sequence for that quest.
  • They are party-scoped: per-player targeting via satisfyingPlayers resolves to nobody here, so use party-wide or named targets.
  • Effects beyond the per-trigger cap are dropped at apply time.
  • The story phase sees them, so their consequences can be narrated.

Common uses: reveal the next quest in a chain (quest-plan, quest-init, quest-accept), grant rewards (player-resource, player-experience), or start a narrative event (narrative-event-start).

Detail Types

Type Location Handling Auto-Trigger
basic AI generates via spatialRelationship No - needs manual triggers
detailed Uses exact questLocation; locationless (objective mode from the start) when omitted Yes - from a story-type completionCondition

Spatial Relationships

For basic quests, defines where the AI generates the quest location:

Relationship 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

Auto-Generated Triggers

For any quest with a story-type completionCondition, the system auto-generates a hidden completion trigger:

{
  name: `__generated_quest_progress__/${questId}`,
  recurring: false,
  conditions: [{
    type: 'story',
    query: completionCondition.query,  // Used directly as the semantic trigger query
  }],
  effects: [{
    type: 'quest-progress',
    questId,
  }],
}

If the query is empty or whitespace, no trigger is generated — the world creator must create triggers manually in tabs/triggers.json.

Trigger names beginning __generated_quest_progress__/ are reserved for these engine-generated quest progress triggers; author your own triggers under other names.

narrative-event-completed conditions build no trigger. Instead, the quest completes (if accepted) once the referenced narrative event completes.

Basic quests never get auto-generated triggers regardless of completionCondition.

Making Quests Available

Quests start as hidden unless their authored status says otherwise (see Starting Status above). Ways to move them along:

Via Story Start:

{
  "storyStarts": {
    "adventure": {
      "startingQuests": ["rescue-princess", "investigate-ruins"]
    }
  }
}

startingQuests entries resolve by quest file id or unique name. At story start, hidden quests listed here are promoted to available; quests already accepted (for example via an authored status: 'accepted') are skipped.

Note: firstQuest is a separate freeform text field for AI quest generation - it doesn’t reference predefined quests.

Via Trigger Effect:

{
  "triggers": {
    "unlock-quest": {
      "conditions": [{ "type": "story", "query": "Player speaks to the king" }],
      "effects": [{ "type": "quest-init", "operator": "set", "value": "rescue-princess" }]
    }
  }
}

Trigger Effects and Conditions for Quests

Triggers can manipulate quests through these effects (full semantics in the triggers reference — see the triggers skill):

Effect Parameters What it does
quest-plan operator: 'set', value (quest file id) Moves a hidden quest to planned, reserving its storyline against duplicate AI generation
quest-init operator: 'set', value (quest file id) Moves a hidden or planned quest to available (puts it on offer)
quest-accept operator: 'set', value (quest file id) Accepts a hidden, planned, or available predefined quest outright
quest-objective-reveal questId, objectiveId Sets the objective active, makes it the quest’s active objective, sets the quest’s next step to its text
quest-objective-complete questId, objectiveId Marks the objective completed, advances the next step to the next active objective (or clears it)
quest-next-step-set questId, text, source Sets the quest’s next step directly
quest-next-step-clear questId Clears the quest’s next step
quest-complete questId Silently marks the quest’s main objective done — same as quest-progress but with no player-visible status line; the quest then completes on the same turn if accepted
party-next-step-set text, source Party-level guidance shown before any quest is visible
party-next-step-clear — Clears party-level guidance

The quest-status condition (questId, operator, value) compares against the quest’s status (hidden, planned, available, expired, accepted, completed, abandoned, rejected).

Quest references (quest-plan / quest-init / quest-accept values, questId fields on quest-* effects and the quest-status condition, and startingQuests entries) resolve by runtime quest id first, then quest file id, then unique display name. The quest file id is the stable reference to author.

Interaction with Narrative Events

While a narrative event is active, generated-quest creation is fully suppressed — the engine will not offer new generated quests mid-event. Authored quest triggers still fire normally.

AI Task Usage

Task Reads Writes
Game initialization quests (definitions) quests (instances)
generateProblemDetails spatialRelationship, questDesignBrief questLocation, questAreas, startingArea
generateNewQuests planned and available quests (duplicate avoidance) Creates new Quest instances
Trigger effects status status, objectives, next-step changes
UI All fields for quest log -

Cross-References

Field References
questLocation tabs/locations.json
completionCondition.eventId Keys in the narrativeEvents world section (see the narrative-events skill)
startingQuests Quest file ids or unique names in tabs/quests.json (from story-starts.json)
firstQuest Freeform text instruction (NOT a quest key)
Quest keys Referenced by trigger effects quest-plan, quest-init, quest-accept, quest-objective-reveal, quest-objective-complete, quest-next-step-set, quest-next-step-clear, quest-complete and condition quest-status (see the triggers skill)
onCompleteEffects[] fields Same references as trigger effects — see the triggers reference Cross-References table

This site uses Just the Docs, a documentation theme for Jekyll.