godot-quest-system

Expert blueprint for quest tracking systems (objectives, progress, rewards, branching chains) using Resource-based quests, signal-driven updates, and AutoLoad managers. Use when implementing RPG quests or mission systems. Keywords quest, objectives, Quest Resource, QuestObjective, signal-driven, branching, rewards, AutoLoad.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "godot-quest-system" with this command: npx skills add thedivergentai/gd-agentic-skills/thedivergentai-gd-agentic-skills-godot-quest-system

Quest System

Resource-based data, signal-driven updates, and AutoLoad coordination define scalable quest architectures.

Available Scripts

quest_manager.gd

Expert AutoLoad quest tracker with objective progression and reward distribution.

quest_graph_manager.gd

Runtime manager for graph-based quests. Tracks objectives and node progression.

NEVER Do in Quest Systems

  • NEVER store quest data in nodes — Quest progress in player.gd variables? Loss on scene reload. Use Resource-based Quests OR AutoLoad singleton.
  • NEVER use strings for quest IDs without registryupdate_objective("kil_bandts", ...) typo = silent failure. Use constants OR validate IDs against registry.
  • NEVER forget to disconnect signals — Quest completed but signal still connected? Quest completes again on next update = duplicate rewards. Disconnect in _on_quest_completed().
  • NEVER poll for objective updates — Checking if enemy_count == 10 every frame = wasteful. Use signals: enemy.died.connect(quest_manager.on_enemy_killed).
  • NEVER skip save/load for quests — Player completes quest 5, game restarts, quest resets? Frustration. MUST persist active_quests + completed_quests arrays.
  • NEVER use all() for objectives without null checkobjectives.all(func(obj): return obj.is_complete()) with null objectives? Crash. Validate array contents first.

# quest.gd
class_name Quest
extends Resource

signal progress_updated(objective_id: String, progress: int)signal completed

@export var quest_id: String
@export var quest_name: String
@export_multiline var description: String
@export var objectives: Array[QuestObjective] = []
@export var rewards: Array[QuestReward] = []
@export var required_level: int = 1

func is_complete() -> bool:
    return objectives.all(func(obj): return obj.is_complete())

func check_completion() -> void:
    if is_complete():
        completed.emit()

Quest Objectives

# quest_objective.gd
class_name QuestObjective
extends Resource

enum Type { KILL, COLLECT, TALK, REACH }

@export var objective_id: String
@export var type: Type
@export var target: String  # Enemy name, item ID, NPC name, location
@export var required_amount: int = 1
@export var current_amount: int = 0

func progress(amount: int = 1) -> void:
    current_amount += amount
    current_amount = mini(current_amount, required_amount)

func is_complete() -> bool:
    return current_amount >= required_amount

Quest Manager

# quest_manager.gd (AutoLoad)
extends Node

signal quest_accepted(quest: Quest)
signal quest_completed(quest: Quest)
signal objective_updated(quest: Quest, objective: QuestObjective)

var active_quests: Array[Quest] = []
var completed_quests: Array[String] = []

func accept_quest(quest: Quest) -> void:
    if quest.quest_id in completed_quests:
        return
    
    active_quests.append(quest)
    quest.completed.connect(func(): _on_quest_completed(quest))
    quest_accepted.emit(quest)

func _on_quest_completed(quest: Quest) -> void:
    active_quests.erase(quest)
    completed_quests.append(quest.quest_id)
    
    # Give rewards
    for reward in quest.rewards:
        reward.grant()
    
    quest_completed.emit(quest)

func update_objective(quest_id: String, objective_id: String, amount: int = 1) -> void:
    for quest in active_quests:
        if quest.quest_id == quest_id:
            for obj in quest.objectives:
                if obj.objective_id == objective_id:
                    obj.progress(amount)
                    objective_updated.emit(quest, obj)
                    quest.check_completion()
                    return

func get_active_quest(quest_id: String) -> Quest:
    for quest in active_quests:
        if quest.quest_id == quest_id:
            return quest
    return null

Quest Triggers

# Example: Kill quest integration
# enemy.gd
func _on_died() -> void:
    QuestManager.update_objective("kill_bandits", "kill_bandit", 1)

# Example: Collection integration
# item_pickup.gd
func _on_collected() -> void:
    QuestManager.update_objective("gather_herbs", "collect_herb", 1)

# Example: Talk integration
# npc.gd
func interact() -> void:
    DialogueManager.start_dialogue(dialogue_id)
    QuestManager.update_objective("find_elder", "talk_to_elder", 1)

Quest UI

# quest_ui.gd
extends Control

@onready var quest_list := $QuestList

func _ready() -> void:
    QuestManager.quest_accepted.connect(_on_quest_accepted)
    QuestManager.objective_updated.connect(_on_objective_updated)
    refresh_ui()

func refresh_ui() -> void:
    for child in quest_list.get_children():
        child.queue_free()
    
    for quest in QuestManager.active_quests:
        var quest_entry := create_quest_entry(quest)
        quest_list.add_child(quest_entry)

func create_quest_entry(quest: Quest) -> Control:
    var entry := VBoxContainer.new()
    
    var title := Label.new()
    title.text = quest.quest_name
    entry.add_child(title)
    
    for obj in quest.objectives:
        var obj_label := Label.new()
        obj_label.text = "%s: %d/%d" % [obj.target, obj.current_amount, obj.required_amount]
        entry.add_child(obj_label)
    
    return entry

Best Practices

  1. Signal-Driven - Emit events, systems listen
  2. Save Progress - Track completed quests
  3. Validation - Check prerequisites before accepting

Reference

  • Related: godot-dialogue-system, godot-save-load-systems

Related

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

godot-master

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

godot-shaders-basics

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

godot-ui-theming

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

godot-particles

No summary provided by upstream source.

Repository SourceNeeds Review