Loading...
Loading...
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.
npx skill4agent add thedivergentai/gd-agentic-skills godot-quest-systemplayer.gdupdate_objective("kil_bandts", ...)_on_quest_completed()if enemy_count == 10enemy.died.connect(quest_manager.on_enemy_killed)active_questscompleted_questsall()objectives.all(func(obj): return obj.is_complete())# 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_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.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# 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.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 entrygodot-dialogue-systemgodot-save-load-systems