Skip to content

08. Quests tool: Odyssey

Ketei edited this page Dec 26, 2025 · 6 revisions

I. Introduction and Core Principles

The Quest Tool (Odyssey) is a management system designed to handle the creation, management and tracking of quests.

1.1 Purpose and Philosophy: Blueprints vs. State

Odyssey consist of 2 parts:

  • The Blueprint (Quest Resource): A static template defining the narrative structure, stages, and specific objectives.
  • The Tracker (Quest Manager): The runtime object (NexusForge.Quests) that tracks active quest states, manages progression, and maintains a historical log of completed tasks.

1.2 Data Model Hierarchy

  • Quest: The top-level container for a specific storyline or mission.
  • Stage: A major milestone within a quest. A quest can contain any number of stages.
  • Objective: A specific task within a stage. A stage can contain any number of objectives.
  • Requirements: Each Objective contains N requirements consisting of an Identifier (string), a Value (variant), and a Comparator (e.g., ==, >=, <).

1.3 Storage and Workflow

  • File-Based: Each quest is saved as an individual .tres resource file.
  • Dynamic Support: The QuestManager tracks Quest objects rather than files, allowing for dynamically generated quests created via code at runtime to be properly tracked and logged.

II. The Editor Interface

The Odyssey editor is a three-column workspace designed for rapid quest development. It features a dynamic breadcrumb navigation at the top (e.g., new_quest / initial_stage / first_objective) to track the current hierarchy level.


2.1 Column 1: Navigation & Management

The left column handles file operations and the structural hierarchy of the open quest.

  • Files Section:
    • New Quest Button: Located next to the "Search Files" bar; use this to create new quest resource files.
    • Quest List: Displays active quest files; an asterisk (*) indicates unsaved changes. Hovering over an item displays its full path.
  • Quest Tree:
    • Use the [+] icons to add new Stages to the Quest or new Objectives to a Stage.
    • Selected items are highlighted, updating the data displayed in the other two columns.
  • Context (Right-Click) Menu:
    • Add Stage/Objective: Quickly append new children to the hierarchy.
    • Edit ID: Modify the unique internal identifier for the resource.
    • Duplicate: Create a copy of the selected item and its children.
    • Remove: Delete the selected item from the hierarchy.
    • Set as Entry: (For Stages) Explicitly define this stage as the starting point of the quest.

Note

The visual order of items in the tree is for organizational purposes only and does not dictate the flow of the quest. This layout is saved within the .godot folder; therefore, the specific tree order is not preserved if the resource is opened on a different computer.


2.2 Column 2: Core Data & Custom Metadata

The middle column focuses on the descriptive properties and custom data of the selected resource.

  • Identity Section:
    • Type Dropdown: Categorize the item (e.g., Main Quest, Side Quest, or Stage Types like Travel, Status, Collect).
    • Pencil Icon: Opens the underlying script for quick enum updates.
    • Title & Description: Multi-line text fields for player-facing content.
  • Custom Data Section:
    • A versatile dictionary editor for adding metadata.
    • Quick-Add Buttons: Specific icons to create Folder, Integer, Float, Boolean, or String variables.
    • Variables can be filtered using the "Search Custom Data" bar.

Tip

You can edit the default custom data newly created quests have by modifying the custom_data variable on the respective quest[1], stage[2] and objective[3] resource file.


2.3 Column 3: Logic, Flow & Events

The right column is context-sensitive, shifting its interface based on the hierarchy level selected in Column 1.

When a Quest is Selected:

  • Quest Events: Manage global Success and Failure events. These are organized in a tree structure allowing for nested folders and variables (e.g., a rewards folder containing an experience integer and an items dictionary).

When a Stage is Selected:

  • Flow Targets:
    • Success Target: Dropdown to select which stage follows upon successful completion.
    • Failure Target: Dropdown to select the path taken if the stage fails (or "Quest End" to terminate).
  • Stage Events: Success/Failure event tree specific to this milestone.

When an Objective is Selected:

  • Objective Required: A checkbox at the top determines if this task must be finished for the stage to complete.
  • Requirements Editor:
    • Type Toggle: Click the data type icon (e.g., int, flt, bool, Str) to change the requirement type.
    • Comparator Selection: Click the operator icon (e.g., ==, !=, >=, <) to choose the comparison logic.
    • Target Value: Define the goal value that progress must reach to satisfy the requirement.
  • Objective Events: Success/Failure event tree specific to this task.

III. Progression Logic & Flow Control

Odyssey operates on a hierarchical evaluation chain. While the resources define the "rules" and structure of a quest, the QuestManager acts as the actor that executes those rules to transition through the quest.


3.1 Objective Evaluation

An objective is the most granular unit of logic. Its completion status is determined by evaluating live data against the quest's requirements:

  • Requirement Matching: The QuestManager tracks the current progress for each objective. When the progress of an objective changes, the objective resource compares that progress against its requirements list. An objective is only considered "complete" if every registered requirement passes its logic gate (e.g., OP_EQUAL, OP_GREATER_EQUAL).
  • Manual Override: Calling set_completed(true) on an objective resource forces its state to complete. This allows bypassing requirement logic for purely scripted or narrative events.

Important

Type Safety: When updating progress, the system validates that the data type of the update matches the type defined in the requirement. Mismatched types are rejected to prevent errors at runtime.


3.2 Stage Logic & The "Required" Rule

The QuestStage resource is a stateless blueprint; it defines the criteria for progression but does not track its own active state. The QuestManager uses the stage's internal configuration to govern the flow:

  • Mandatory Objectives: If an objective is registered to a stage as Required, the QuestManager will not progress the quest automatically until that specific objective is completed.
  • Optional Objectives: Objectives not marked as required do not block progression. The manager allows a stage to finish even if these tasks are incomplete, though their individual success or failure is still recorded in the logs.

3.3 The Auto-Advance Flow

If auto_advance_stages is enabled when a quest is started, the QuestManager automates the transition between milestones. Every time an objective is updated via set_objective_progress, the manager performs the following sequence:

  1. Validation: The manager checks if the stage can be completed. This acts as a logic gate, succeeding only if every mandatory objective for that stage is finished.
  2. Success Branching: If the check passes, the manager identifies the stage's success_stage_id. If a valid ID is found, the quest advances and emits quest_progressed.
  3. Terminal Check: If the logic passes but the target stage ID (either success_stage_id or failure_stage_id) is empty, the manager treats this as the end of the quest line. It finalizes the quest, emits quest_finished, and moves the data to the history log.

3.4 Logic Hierarchy Reference

Level Logic Source Executioner Result
Requirement Objective Resource QuestManager Validates a single value/operator pair.
Objective Objective Resource QuestManager Emits objective_completed when all requirements pass.
Stage Stage Resource QuestManager Checks for stage completion to authorize a transition. Emits stage_completed when all objectives are completed.
Quest Quest Resource QuestManager Emits quest_finished when a final stage is reached.

IV. Quest, Stages & Objective types

To define the list of types each object can be (quest, stage or objective) you need to update the relevan enum of each script. This can be accessed by pressing the pencil button next to the types dropdown when the relevant item is selected on the quest structure tree (bottom left).

Warning

Types are defined as enum, but stored as integers. When editing Quest/Stage/Objective types, always Append new items and avoid removing items or use Explicit Values (e.g., SIDE = 2). Changing the order of an existing Enum will cause saved files to point to the wrong types if they weren't explicitly declared.


V. Runtime API & GDScript Usage

The QuestManager handles both file-based and dynamically generated Quest objects.

V. Runtime API & GDScript Usage

5.1 Signals

These signals are emitted by the QuestManager to allow game systems (UI, World, Dialogue) to react to progression.

Signal Description
quest_started Emitted when a quest is successfully initialized.
quest_progressed Emitted when a quest moves to a new stage (including the entry stage).
quest_finished Emitted when a quest ends (either via Success or Failure).
stage_completed Emitted when a stage is finalized.
objective_completed Emitted when an individual objective is finished.
quest_event_triggered Emitted when a Success/Failure event from any level is triggered.

5.2 QuestManager Methods

The central hub for managing and persisting all quest data at runtime.

Method Return Type Description
start_quest(quest, auto_advance) Begins tracking a quest. If auto_advance is true, the manager handles stage transitions automatically based on objective completion.
remove_quest(id, clear_history) Stops tracking an active quest. Can optionally wipe the quest from the historical log.
get_quest(quest_id) Quest Retrieves the active Quest resource object. Returns null if the quest is not active.
get_quest_current_stage(id) QuestStage Returns stage object of the stage the player is currently on for the specified quest.
get_quest_current_stage_id(id) StringName Returns the ID of the stage the player is currently on for the specified quest.
is_quest_active(id) bool Returns true if the quest is currently being tracked in the active dictionary.
set_objective_progress(...) Updates requirement values. If all requirements are met, it completes the objective and may advance the stage if auto_advance is enabled.
complete_objective(...) Forces completion of a specific objective with a manual success/failure status.
complete_stage(...) Forces completion of a stage and advances the quest to the next logical path.
complete_quest(id, success) Finalizes the entire quest and triggers the associated global quest events.
get_quests_data() Dictionary Serializes all active quest states, duplicated resources, and the history log for saving.
set_quests_data(data) Restores the manager state, log, and quest progress from a save dictionary.
quest_success_status(id) SuccessStatus (Enum) Checks the log to see if a quest finished successfully or failed.
stage_success_status(...) SuccessStatus (Enum) Checks the log for the success/failure status of a specific stage.
objective_success_status(...) SuccessStatus (Enum) Checks the log for the success/failure status of a specific objective.
erase_quest_from_log(id) Removes the history log for a specific quest.
clear_quest_log() Completely wipes all historical records from the manager.

5.3 Quest (Resource) Methods

The static blueprint defining the quest structure and initial entry point.

Method Return Type Description
stages() Array[StringName] Returns an array containing the IDs of all stages registered to this quest.
add_stage(stage) Adds a QuestStage to the quest. Overwrites any existing stage with the same ID.
remove_stage(stage_id) Removes a stage from the quest.
has_stage(stage_id) bool Returns true if the quest contains a stage with the given ID.
get_stage(stage_id) QuestStage Returns the QuestStage resource or null if it does not exist.

5.4 QuestStage (Resource) Methods

Manages the logic for a specific milestone and determines the branching path.

Method Return Type Description
objectives() Array[StringName] Returns an array of IDs for all objectives registered to this stage.
add_objective(objective, required) Registers a new objective. The required flag determines if it blocks stage completion.
remove_objective(objective_id) Removes an objective from the stage.
has_objective(objective_id) bool Returns true if the objective is registered in this stage.
set_objective_required(...) Updates the "required" status of an existing objective ID.
is_objective_required(id) bool Returns true if the objective must be completed to finish the stage.
can_complete_stage() bool Logic check that returns true only if every objective marked as Required is complete.
get_objective(objective_id) QuestObjective Returns the objective resource or null if not found.

5.5 QuestObjective (Resource) Methods

Handles requirement evaluation and progress validation.

Method Return Type Description
requirements() Array[String] Returns an array of all requirement IDs (keys) for this objective.
get_requirement_type(id) int Returns the typeof the requirement value (Godot TYPE_* constants).
get_requirement_value(id) Variant Returns the goal/target value for a specific requirement.
get_requirement_mode(id) int Returns the operator used for comparison (e.g., OP_EQUAL).
set_requirement(...) Configures a requirement gate. Resets progress if the new value type differs from the old one.
set_progress(id, value) Updates the internal progress tracker. Rejects values that do not match the requirement type.
get_objective_progress() Dictionary Returns a dictionary containing the mode, current value, and target value for every requirement.
get_requirement_progress(id) Dictionary Returns the progress dictionary for a specific individual requirement.
is_objective_complete() bool Evaluates all requirements based on current progress. Returns true if all logic gates pass.
has_requirement(id) bool Returns true if the logic gate ID exists in this objective.
clear_requirements() Clears all requirements and resets the internal progress dictionary.
set_completed(is_completed) Manual override to force the objective's completion state, bypassing requirements.

VI. Implementation Examples

Odyssey is designed to be reactive. By connecting to the QuestManager signals, you can trigger world changes, UI updates, and reward systems without tightly coupling your quest data to your game's core systems.


6.1 Handling Quest Rewards

Odyssey uses a Success/Failure Event system to pass data dictionaries to your game logic. This is the primary way to handle experience, currency, item rewards or any other event.

Editor Setup: In the Events section of a Quest, Stage, or Objective, define a key named rewards in the On Success Events dictionary.

events

GDScript Implementation:

func _ready() -> void:
    # Connect to the global QuestManager (Singleton)
    NexusForge.Quests.quest_event_triggered.connect(_on_quest_event_triggered)

func _on_quest_event_triggered(event_id: String, event_data: Variant) -> void:
    match event_id:
        "rewards":
            # event_data contains the dictionary: 
            # {"experience": 500, "items": {"iron_sword": 1, "health_potion": 2}}
            
            if event_data.has("experience"):
                PlayerStats.add_xp(event_data.experience)
            
            if event_data.has("items"):
                for item_id in event_data.items:
                    var count = event_data.items[item_id]
                    Inventory.add_item(item_id, count)
                    
        "play_stinger":
            AudioPlayer.play_sfx("quest_complete_fanfare")

6.2 Updating Objective Progress

To ensure the manager correctly tracks requirements and triggers auto-advancement, always use NexusForge.Quests.set_objective_progress rather than modifying the resource directly.

Example: A Mob Kill Quest

func _on_enemy_died(enemy_type: String) -> void:
	if enemy_type == "Goblin":
		# Get the current progress to increment it
		var current_stage: QuestStage = NexusForge.Quests.get_quest_current_stage(&"main_story")
		var current_objective: QuestObjective = current_stage.get_objective(&"kill_goblins")
		var objective_progress: Dictionary = current_objective.get_objective_progress("goblin_count")
		var current_kills: int = objective_progress.current # or objective_progress["current"]
        
		# Update the manager; this will automatically check if the objective is complete
		NexusForge.Quests.set_objective_progress(
				&"main_story", 
				&"prologue", 
				&"kill_goblins", 
				"goblin_count", # The Requirement ID
				current_kills + 1)

6.3 Handling Branching Narratives

Because the QuestManager emits quest_progressed every time the stage changes, you can use this to update your world state (e.g., spawning NPCs or opening gates) based on the specific path the player took.

func _on_quest_progressed(quest_id: StringName, to_stage: StringName) -> void:
	if quest_id == &"kings_gambit":
		match to_stage:
			&"storm_the_castle":
				WorldManager.set_castle_gate_open(true)
				WorldManager.spawn_reinforcements()
			&"stealth_approach":
				WorldManager.set_guards_alert_level("low")
				WorldManager.enable_secret_passage()

6.4 Save and Load Logic

Using get_quests_data() and set_quests_data(), you can easily integrate Odyssey into your existing save system.

func save_game() -> void:
    var save_file = FileAccess.open("user://savegame.dat", FileAccess.WRITE)
    var all_data = {
        "player_pos": player.position,
        "quests": NexusForge.Quests.get_quests_data() # Captures active quests and history
    }
    save_file.store_var(all_data)

func load_game() -> void:
    var save_file = FileAccess.open("user://savegame.dat", FileAccess.READ)
    var data = save_file.get_var()
    # Restores all quest resources, requirements, and logs
    NexusForge.Quests.set_quests_data(data["quests"]) 

VII. Exporting & Validation

The Odyssey export plugin performs automated integrity checks to prevent game-breaking logic errors.

7.1 Automated Validation

  • Duplicate ID Check: Issues a warning if two quest resources share the same internal ID.
  • Entry Stage Check: Verifies that the defined entry_stage actually exists within the quest's stage dictionary.

VII. Footnotes

  1. Default path for the quest resource is res://addons/nexus_forge/resources/quest_resource.gd.
  2. Default path for the stage resource is res://addons/nexus_forge/resources/quest_stage.gd.
  3. Default path for the objective resource is res://addons/nexus_forge/resources/quest_objective.gd.

Clone this wiki locally