[Need Advice!] Quest system implementation advice needed

The Problem

I am currently making a 2D top-down RPG game in which player can accept quests from NPC, I need my quest to have some kind of reference to the NPC who gave player the quest, so player can return to when quest is completed and perform some interaction whit the NPC, the reference should be easy to save and load when needed. I am currently want to save all my quests into csv file for easy editing. So I am looking for advice on what to choose as a reference.

My Thoughts

  • Using a reference to the NPC node
    In this way I can easily access to NPCs build-in functions to do something when quest is completed. But saving a node to file, it seems I can only use Resource to do the job, and it just don`t feel good to do it this way.

  • Using NPCs name which is a String as a reference
    In this way saving the quest data will be easier but harder to access to the NPC node, I will have to build another system to get access to the node itself which does
    not make it easier.

  • Using a quest list that is local to each NPC
    In this case saving and loading the quests could be done along with saving and loading the NPC itself when changing game levels, when player interact with a specific NPC it can access the quests that is specific to that NPC thus achieve reference in a ‘soft’ way.

Looking for any advice

I hope I express my thoughts clear enough, any advice will be helpful.

1 Like

You could implement it by adding a node to Autoload for handling the nodes you want.
And you could also call functions (let’s say for example, adding a quest to the player character’s journal or for mark certain quest completed) or changing values to variables anywhere you want because of this.
(eg.: get_node(/root/TheNode).add_quest("Quest name") or something)

Doesn’t this go in the help category?

I would do the following:

  • Create an autoload to handle all of the quest system
  • Saving the NPC’s id (instead of name) as a string
  • When the game loads, each NPC can make a call to the quest system, and register itself

What does the register do? it passes the id and reference to itself, so it can ‘rebuild’ the id-node dictionary, helping you have the references in a ‘decoupled’ way.

The id-registration approach (@huevoquilmes) is the right direction, but there are a few traps worth knowing since we built exactly this:

Use a stable string ID, not the node name. Node names change and collide. Give each NPC a quest_giver_id: String that never changes — that’s what you serialize, in both CSV (quest definitions) and your save file (quest progress).

Separate two layers — the original post mixes them:

  • Quest definitions (what quests exist, giver id, objectives) → static data, CSV is fine
  • Quest progress (accepted/completed, counters) → runtime state, save as JSON per slot

Never serialize node references. At runtime, keep a registry instead:

# QuestManager (autoload)
var givers := {}  # id -> node

func register_giver(id: String, node: Node) -> void:
    givers[id] = node

func unregister_giver(id: String) -> void:
    givers.erase(id)

Traps the existing replies skip:

  1. NPC _ready() order isn’t guaranteed — if you restore quest state before NPCs register, lookups fail. Restore progress after the scene is up, or queue lookups and resolve deferred.
  2. NPCs must unregister in _exit_tree(), or a re-entered scene double-registers and you hold stale references.
  3. If a quest is completed but its giver isn’t in the tree (different level), the “return to NPC” interaction must survive that — store turn_in_id in progress and resolve it whenever the NPC registers.

This is exactly the design we ended up with in a quest plugin I built for Godot 4 (.NET) — LiteQuest does the id-registry + JSON progress save part out of the box. But even rolling your own, the three traps above are the ones that bite in week 2.