gameplay/questState.js

Gameplay — Quests

QuestManager — multi-phase quest definitions, kill objectives, advancePhase behavior, and the last-phase gotcha.

Gameplay — Quests

QuestManager handles multi-phase quests with typed objectives. Quests are defined as data, registered at game startup, and tracked through phases as the player completes objectives.

Include

<script src="/js/dn/gameplay/questState.js"></script>

Quest Definition

const QUESTS = {
  goblin_bounty: {
    id:    'goblin_bounty',
    title: 'Goblin Problem',
    phases: [
      {
        description: 'Kill 3 goblins threatening the village.',
        objectives: [
          {
            type:     'kill',
            targetId: 'goblin',     // matches entity tags
            count:    3,
            label:    'Goblins killed',
          }
        ]
      }
    ],
    rewards: {
      gold: 50,
      items: [{ itemId: 'iron_bar', count: 2 }],
      xp: 100,
    }
  }
};

API

const quests = new QuestManager(QUESTS);

// Register and start a quest
quests.startQuest('goblin_bounty');

// Report a kill — automatically advances kill objectives
quests.reportKill('goblin');

// Check quest status
quests.getStatus('goblin_bounty');  // 'available' | 'active' | 'complete' | 'turned_in'

// Check phase progress
const q = quests.getQuest('goblin_bounty');
q.currentPhase;        // 0-based index
q.phases[0].objectives[0].current;  // kill count so far
q.phases[0].objectives[0].count;    // kill count target

// Turn in (call from dialogue side effect or directly)
quests.turnIn('goblin_bounty');   // grants rewards, sets status to 'turned_in'

advancePhase Behavior

advancePhase() increments currentPhase and:

  • If advancing to the last phase: sets status = 'complete' — the quest is ready to turn in.
  • If advancing an earlier phase: just increments currentPhase, status stays 'active'.

This means the final objective must be on the last phase. If you add a trailing “return to NPC” phase after a kill phase, the kill completion will advance to the return phase (status stays 'active') — the player won’t be able to turn in until they interact with the NPC a second time.

Recommended pattern: Collapse kill-and-return into a single phase with the kill objective. The kill meeting the count auto-advances the last phase to 'complete', which unlocks questStatus: 'complete' dialogue conditions immediately.

// ✅ One phase — kill count completes it, quest becomes 'complete' instantly
phases: [
  {
    description: 'Kill 3 goblins.',
    objectives: [{ type: 'kill', targetId: 'goblin', count: 3, label: 'Goblins killed' }]
  }
]

// ❌ Two phases — kill advances to phase 1 (status stays 'active'), 
//    player must return and interact to turn in
phases: [
  {
    description: 'Kill 3 goblins.',
    objectives: [{ type: 'kill', targetId: 'goblin', count: 3, label: 'Goblins killed' }]
  },
  {
    description: 'Return to the Elder.',
    objectives: [{ type: 'interact', targetId: 'bjorn' }]  // creates the off-by-one
  }
]

Multi-Phase Quests

For quests that genuinely need multiple phases (e.g., gather → craft → deliver), use them:

phases: [
  {
    description: 'Gather 5 iron ore.',
    objectives: [{ type: 'gather', itemId: 'iron_ore', count: 5, label: 'Iron ore gathered' }]
  },
  {
    description: 'Smelt 3 iron bars.',
    objectives: [{ type: 'gather', itemId: 'iron_bar', count: 3, label: 'Iron bars smelted' }]
  },
  {
    description: 'Deliver the bars to the Blacksmith.',
    objectives: []    // last phase — completing this phase triggers 'complete'
  }
]

Phase 0 auto-advances when iron_ore count reaches 5 (via reportGather() or inventory tracking). Phase 2 (last) gets triggered by the interaction with the Blacksmith NPC — which calls quests.advancePhase('mining_quest') directly.

Mission Tracker HUD

The gameplay framework includes a MissionTrackerModule that renders active + completable quests top-left on screen. It automatically interpolates kill counts:

[!] Goblin Problem
    Goblins killed: 2/3

When a quest is completable, it shows with a ✓ indicator and the NPC name to return to.

Quest Conditions in Dialogue

Use quest status as a dialogue condition to gate choices:

condition: { type: 'questStatus', questId: 'goblin_bounty', status: 'complete' }

Valid status values: 'available' (registered, not started), 'active' (in progress), 'complete' (objectives met, not yet turned in), 'turned_in' (rewarded and closed).