Thinking through design pattern for NPC distribution

I’m working on a fairly bog-standard RPG, and I’m trying to work out a really robust workflow to position NPCs throughout the world and dynamically change their spawn location and dialogue in response to player activities or progression of the main quest/world state. This is mostly thinking out loud, but I’d really appreciate some feedback if there are obvious flaws to the architecture I’ve built.

Right now I’m accomplishing this by building a giant dict containing every NPC in the game accessible by enum key, and giving the NPC itself keys for its default spawn location and default dialogue. Temporary changes to either, like a quest moving someone across the world or changing their dialogue to read “I don’t have time to talk, darn it, I need those peanuts you promised me!”, are stored as a <NPC key, dialogue/spawn override> pair in the actual quest node that causes these changes. More permanent changes, like moving around in response to the main quest, are achieved by using scripting commands to manually change an NPC’s default keys.

With this all set up, every time a level is loaded in or someone is spoken to, the game iterates through every quest looking for override spawn/dialogue keys. If it finds some, it sorts them by priority and returns the highest-priority override, and if it finds nothing it spawns them at their default location, or plays the default dialogue.

All in all this feels like a fairly robust design, since linking behavior to individual quests/quest nodes means that debugging unwanted behavior is as simple as checking that NPC’s overrides, and because it simplifies my workflow as a content creator by focusing everything on the quest editor, instead of forcing me to juggle multiple tables and scripting injectors across different parts of the game. That having been said, I am literally pulling this entire setup out of thin air, and there are no formal patterns or architectural standards I’m building towards; is there anything about this that’s likely to bite me in the rear end as complexity scales, or is this a fairly okay way to approach a complex problem?

To save checking every quest giver when someone is spoken to you could have them register to an event delegate for when that conversation occurs. This would avoid having any delay if the game had too many npcs. Also means if you need other things to occur from an event they can add themselves to it easily.

That’s a good idea… you’re thinking whatever gets invoked when they’re interacted with/spawned is essentially an empty listener, and the delegates would reroute that message? I kinda-sorta have that going on right now, insofar as there’s an observer pattern set up that issues a message whenever any object that can accept or transmit scripting commands does something, but it effectively operates in parallel to the main system- talking with an NPC sends a message informing anyone listening to the observer that the PC just spoke to NPC Bob or whomever, but Bob’s dialogue default/override is assessed and retrieved separately.