Data-driven approach to letting quests effect world state

This is partially just thinking out loud, but I have a node-based quest system for an RPG that I’m relatively happy with; I built a visual editor that lets me built some given quest’s node graph with a point & click interface and then convert the final product into JSON. The issue is that while expressing and monitoring quest objectives is trivial, I’m having a lot of trouble coming up with a clean workflow to actually effect the world’s state in response to what the player is supposed to be doing.

Most of the heavy lifting is done inside the game’s cutscenes via scripting commands embedded everywhere, so the primary purpose of the quest’s node graph is to keep track of which cutscenes should be loaded- think “Once this quest is activated, Bob the Bartender should always appear sprawled outside his bar until the player speaks with him; ignore his default behavior for now”. The exact same logic also needs to apply to spawning objects in a level (e.g. place flaming cars around the football stadium when the riot quest begins, and keep them there until the quest is resolved), and even to the game state (next time the player enters the tavern, instead of spawning the player in exploration mode and populating the interior with the patrons who the database says are inside, spawn them in combat mode and add a bunch of drunk hooligans to fight).

The way I’m accomplishing this right now is via overrides: whenever the game does something that a quest can effect, it messages every quest with a series of enums to let it know what action the system is trying to accomplish, like loading a specific level or playing an NPC’s dialogue. If a given quest wants to alter the world state (e.g. “spawn the bartender outside his bar, not standing behind the bar indoors”), it does so by specifying a property that it wants to override, the logic to execute in place of the default action, and a numerical priority so that the system can handle multiple quests giving conflicting instructions.

That works, but it’s clunky and incredibly unpleasant to work with, and it’s so excessively complicated that I’m half-expecting weird, hard-to-debug errors to start cropping out of nowhere. That having been said, I’m struggling to find a cleaner way to implement this; are there any standard design patterns that I’m missing here? I’ve spent a lot of time researching, but most of what I’ve found pertains to storing quests themselves in data, and not to actually interfacing between the quest and the game world.

Without knowing exactly how your world loads scenes, you might be able to accomplish an easier approach by having the quests be the driving force. I assume your scene loader is looking up what it needs to do in some file somewhere (sql table, json flat file , whatever). Hopefully each scene has some unique tag identifier. So your quests could create an override file, that would include the unique tag of that scene and whatever modifiers should happen. You could create system of tags that would include additions, subtractions, complete overwrite.

Then when your scene loader went to load the scene, it would first check the override file to see if the tag existed in there. If it did it would check if it was a complete overwrite, and just load what was in that file. If it had additions / subtractions it would load the main file normally, then perform additions/subtraction as needed.

This way your quest update code could be the driving force in keeping the override file upto date at the exact time it needs to update that file. Then there is no need to constantly query every quest with every scene load.

Hmm, that’s interesting- we kind of do that now, but it’s not as well-organized as your suggestion. Right now our scene loader has a method that returns a list of every <NPC, SpawnPoint> that’s supposed to spawn inside of it, and that method essentially just asks each quest if it has any spawn overrides for that particular scene. It then sorts the overrides by priority, or retrieves the default spawn information if no overrides are present, and returns the highest-priority instruction for that NPC. Here’s a screenshot from our editor, the Spawn Override column in this case is specifying that Nancy the Bartender should spawn in the TestScene scene, at the TestRoomEntrance spawnpoint, if and only if no other quests have a spawn override for Nancy with a higher priority than this one:

This is functional, but part of what makes it so cumbersome to work with is going back and forth between the JSON file I store all of my dialogue/cutscene info in and the quest nodes to make sure everything matches. I suppose I could simplify it by removing the nodes entirely and using scripting commands inside the same JSON that runs the cutscenes, but that would sacrifice some data sanitization- instead of having my overrides work off of dropdown boxes that only contain well-formed character/scene/object IDs, I would have to hand-type the same information as scripting overrides (e.g. '#AddNewSpawnOverride(‘Nancy’, ‘TestScene_TestRoomEntrance’, ‘BartenderQuest’), which introduces a lot more room for typos and user error.