Quest Systems
Build quest tracking with objectives, progress, and rewards.
Introduction
Quest systems give players goals and direction. This guide covers quest definitions, objective tracking, progress updates, completion, and rewards.
Guide 19 of 25
Screenshot
Step-by-Step Guide
Define quest structure
Each quest has: ID, title, description, objectives (array), rewards, prerequisites, and state (inactive, active, completed, failed).
Create objective types
Support multiple objective types: collect items, kill enemies, reach location, talk to NPC. Each tracks progress differently.
Track progress
Update objective progress when relevant events fire. Use the event system: enemyKilled, itemCollected, locationReached.
Show quest UI
Display active quests in a quest log. Show objective progress (3/10 enemies defeated). Update in real-time.
Handle completion
When all objectives complete, mark quest done, give rewards (items, XP, gold), show completion notification.
Chain quests
Use prerequisites to chain quests. Quest B requires Quest A completed. Creates narrative progression and gates content.
FluxScript Example
Here is a complete FluxScript implementation for this guide:
const QUESTS = {
rescue_daughter: {
title: "Rescue Borin Daughter",
objectives: [
{ id: "reach_cave", type: "location", target: "goblin_cave" },
{ id: "kill_goblins", type: "kill", target: "goblin", count: 5 },
{ id: "find_daughter", type: "interact", target: "borin_daughter" }
],
rewards: { gold: 100, xp: 500, items: [{ id: "steel_armor", count: 1 }] }
}
}
questStates = {}
on start {
for questId in QUESTS {
questStates[questId] = { state: "inactive", progress: {} }
}
}
func startQuest(questId) {
questStates[questId].state = "active"
for i = 0 to QUESTS[questId].objectives.length {
obj = QUESTS[questId].objectives[i]
questStates[questId].progress[obj.id] = 0
}
ui.toast("New Quest: " + QUESTS[questId].title)
audio.play("quest_start")
}
on event("enemyKilled", data) {
for questId in QUESTS {
if questStates[questId].state != "active" continue
for obj in QUESTS[questId].objectives {
if obj.type == "kill" && obj.target == data.enemyType {
questStates[questId].progress[obj.id] += 1
checkObjective(questId, obj)
}
}
}
}
func checkObjective(questId, obj) {
progress = questStates[questId].progress[obj.id]
target = obj.count || 1
if progress >= target {
ui.toast("Objective Complete: " + obj.text)
checkQuestComplete(questId)
}
}
func checkQuestComplete(questId) {
allDone = true
for obj in QUESTS[questId].objectives {
target = obj.count || 1
if questStates[questId].progress[obj.id] < target allDone = false
}
if allDone completeQuest(questId)
}
func completeQuest(questId) {
questStates[questId].state = "completed"
r = QUESTS[questId].rewards
if r.gold player.gold += r.gold
if r.xp player.addXP(r.xp)
if r.items for item in r.items inventory.addItem(item.id, item.count)
ui.show("questCompletePanel")
audio.play("quest_complete")
}Node Graph Example
Prefer visual programming? Here is the equivalent Node Graph:
[On Start] --> [Init: quest states (all inactive)]
[Function: startQuest] --> [Set: active] --> [Init: progress]
--> [Toast + Audio]
[On Event "enemyKilled"] --> [For Each Active Quest]
--> [For Each Kill Objective] --> [Branch: target matches]
--> [progress += 1] --> [checkObjective]
[Function: checkObjective] --> [Branch: progress >= target]
+-- True --> [Toast] --> [checkQuestComplete]
[Function: completeQuest] --> [Set: completed]
--> [Give: gold, XP, items] --> [Show: completion panel]Optimization Tips
Keep your implementation fast and efficient with these optimization tips:
- Use event-driven objective tracking. Instead of polling every frame, subscribe to events (enemyKilled, itemCollected).
- Limit active quests to 5-10. More active quests clutter the quest log and overwhelm players.
- Save quest states to cloud. Quest progress is critical — players expect to resume after closing the game.
- Use prerequisites for quest chains. Gate content behind completed quests for narrative progression.
Common Mistakes
Polling for objective completion every frame
Use events instead. Polling 50 objectives per frame is wasteful.
No quest abandonment option
Let players abandon quests they cannot complete. Stuck quests frustrate players.
