Quest Patterns that Fail
Things in the Dark could have been one of the greatest LL quest mods ever.
Unfortunately, it has a lot of bugs. The "modified" version fixes some of them, making it sort of playable.
Almost every quest in TitD is a broken progression blocker. Why does TitD have so many bugs?
Why does Delzaron consistently make mods that have so many faulty quests?
To be fair to Delzaron, he's not the only modder with this problem, he's just the most prolific. I could name a few other authors, with buggy mods, who script their quests in exactly the same way.
When trying to patch up my TitD so I could play through it some more, I started to see various common recurring problems, with how the quests were set up. I think TitD serves as a "how not to" guide on writing quests, and there are some cautionary lessons to be learned from it.
Most broken quests involved a simple mistake or typo, or some non-updated value, but those mistakes all have a common root cause: unnecessary complexity. The quests are not cookies, they are snowflakes, and this massively expands the effort required to write and test them, to the point where it is impossible. Because it was effectively impossible, Delzaron failed.
How can we ensure that quest development remains in the realm of possible, or better still easy? Rather than hard, or impossible?
As mentioned above, the complexity and hardness comes from a few oft-repeated mistakes.
As these common mistakes are so well established, and known to be flaws in so many mods, you'd think that we'd stop seeing them.
Skyrim quests aren't written in a world of automated testing and extensive QA, and they can not easily be continuously updated without upsetting players with running games. Mods get periodic releases, and those releases should be as robust as possible to avoid player angst; quest logic should be as defensive as possible. If it allows the player to cheat, it's better than a quest that easily becomes blocked.
- When you do multiple bad things, you compound the player's pain.
- When you do multiple good things, you reduce the risk of your mod breaking, even if you make a mistake somewhere else.
- The following practices combine together to help stop the root cause of numerous quest bugs that have a common form.
1) Quest stages should be self contained
What I mean by this, is if you do "setstage" on a quest, the quest should be set completely in the correct state. It shouldn't be possible to run setstage and have the quest go into an inconsistent or broken state.
For example, some player dialog becomes available at stage 50, if you have collected 10 mushrooms.
Choosing that dialog runs a fragment that sets another NPC's faction, sets that NPC's location, and puts a key in their inventory. Then it sets quest stage to 60.
What's wrong with this?
The problem is that if you want to test this quest, you cannot simply set the stage to 60, because the NPC won't be set up properly.
If this quest breaks, you cannot simply set the stage to 60, because, again, the NPC won't be set up properly.
How it should have worked is quite simple: the dialog fragment should have done ONE thing, set the quest stage to 60.
Then, the trigger script for stage 60 should have set up the NPC's faction, location and inventory.
In this case, you can test stage 60 of the quest without any additional preparation, the stage is "self contained".
Following this rule avoids so many problems.
The bad pattern occurs when the logic to set up the quest stage is hidden away somewhere in a different stage. This is not good modularity or encapsulation. Quest stages are a developer tool to help organize work, but many modders use them with no clear objective in mind, just because they think they should
2) Quest stages should serve a clear purpose
If you have quest stages that exist without objectives, alarm bells should probably go off. Why does that stage exist? What state is it representing?
In most cases, a quest stage should have a matching quest objective that the player can see, and the stage advances when that objective is met. Such quests are easy to understand as a player, or developer, and are easy to test.
The worst example of this is ping-pong quests that bounce quest stage updates between multiple quests, where none of the stages were strictly necessary, and served only to update a stage on a foreign quest that itself serves no purpose at all but to update back to the previous quest. Multiple quests become entangled for no reason.
However, there are legitimate reasons to have a stage with no objectives, particularly if the goal is to sequence events in a simple way.
A classic example of event sequencing is the multi-step dialog. The modder uses quest stages (without objectives) to make the player have a conversation with an NPC in a certain order. Each time the player addresses the correct topic, and gets down to the right branch, the quest steps forward, unlocking the next topic and disabling the old one. This is a reasonable way to turn the dialogs on and off, because it's easy to set the quest stage to test the dialogs, and the fragments that advance the dialog stages are simple owning-quest setstage calls. Additionally, testing a quest stage in a condition is quite efficient.
This pattern is broken when the modder does strange, random things:
- The progression of stages is erratically numbered, or jumps back and forth between dialog on different quests in a way that makes following it in the CK extremely tiresome.
- The stages actually go down as well as up. Very confusing.
- The stages intermix variant ways to control the quest, sometimes stage number, sometimes a variable.
3) Dialog conditions that involve == on anything but a quest stage are fragile
The rider to this is: counter values can advance unpredictably.
The corollary of this is that dialog conditions that use == on a quest stage are a good thing, but on counters that go up, probably bad, and on values that take on non-integral values, disastrous.
How many mods have you played where you collected one flower too many and the quest become impossible to finish?
Jobs of Skyrim, I'm looking at you!
Things in the Dark has the same problem. Press the button that advances your slave ranking, and you'll skip past ranking 8, never hit the magic slave-ranking value in the dialog conditions and never be able to transition from Daithe to Hashep. Worse, you can't just setstage to start the Hashep quest-line because this quest violates rule (1) above.
The solution to most of these counter bugs is to test >= instead of ==, then if something unexpected happens, your quest can still progress.
And by always using a dialog condition with a stage check that asserts the correct stage, the game won't end up polluted with dialogs that should have gone away because they'd done their job.
Counter values are particularly unreliable in Skyrim, due to threading, and slow Papyrus, and the player's ability to mess things around, you are asking for trouble when you expect counters to take on exact values. You never know whether you will see every counter value that is set, sometimes you might miss several. If you test for 20 petals, the odds of the player being able to get 22 before your check runs are decent. Just because you never see that case in testing doesn't mean it can't happen.
But if the player has to go through a certain dialog to advance the quest, and that dialog only shows up for the exact stage that needs it, that's a good thing, stick to that pattern.
4) Use a coherent numbering scheme and keep stages ordered
When quest stages are numbered 0, 10, 20, 50, 53, 54, 55, 67, 90, 100, and three of those stages are not even used, it's harder to work on that quest and keep it aligned with other quests and dialog conditions than if it used sensible numbering.
It's even worse if a quest's stage numbers differ wildly from its objective numbers.
In the cleanest case, objectives and stages have the same numbering, so you don't have to flip back and forth between stages and objectives to figure out what is being done.
There is no reason not to start with quest stages rising in 100s. This allows you to use a pattern of steps of 5 for cases where you are doing simple dialog sequencing. Then, when you see a value other than a round hundred, you immediately know it's dialog sequencing.
Of course, sometimes you need to add a stage to an established quest, but this really should be a last resort. Using steps of 100 makes this a lot less likely to run into a blockage though. In such cases, you should still strive to always keep objective numbers lined up with quest stages - there's no reason not to do that and not doing it can be enormously confusing.
5) NPCs can move - cope with it
Things in the Dark, and Deadly Pleasures are frequently upset by an NPC being in the wrong place. NPCs walk off and start doing odd things, but the quest demands them to be in a certain room or near a certain marker. In some cases, TitD ports Frabbi out of the city, totally blocking your progression - it's not really what I'm talking about here though - the most obvious issue is NPCs that you have a talk objective with, who will not talk. That said, all kinds of misplaced NPC issue should be considered.
Assumptions that NPCs will be where you expect are highly likely to be wrong - unless you have immediate (short term) measures to get them in place.
If the PC needs to talk to a certain NPC, don't constrain that conversation to only taking place in a specific location. Constrain it by quest stage. That is enough.
It will be easier to test, and work better for the player.
Unless you have a really good reason, and proper mechanics around it, stopping the player from having a conversation just because they're in the wrong room is infuriating, and leads to player confusion again and again. They have no idea why they can't progress the quest, and why the dialog is missing.
They have a quest objective to talk to NPC X, but NPC X has nothing to say!
Avoid that pattern. It's a cause of woe.
When dealing with mods like AI Overhaul, NPCs will walk off to all kinds of odd places. If an NPC must stay put, you need a special "priority 100" quest and AI package to lock them in place.
Even that may not be enough. You may need a way for the PC to summon the NPC "in game".
For example, if your quest requires Frabbi to be in the ruined dwemer pumping station, then port her there at the stage start.
But if the player enters the station, check to see if Frabbi is there. If she is not there, port Frabbi outside the (only) exit of the pumping station, so when the frustrated player leaves, he bumps right into Frabbi and can have the conversation.
Another thing that follows from this is quest markers - if the goal is an NPC, make sure the NPC is the target, not the location that they may not be at. In DP you can arrive at the quest location, but nine times out of ten, the NPC isn't there. As you don't know who you have to speak to, there's no easy workaround.
6) Scenes are your friend - don't make them an enemy
Slaverun has a mechanism for using scenes that allowed Kenjoka to write massive amounts of story content without much in the way of bugs. In comparison, TitD has a small amount of story, and it fails to advance it correctly at almost every single step.
Why did Kenjoka have no problems with this issue in Slaverun Reloaded, but Delzaron hits an iceberg on almost every single quest in Things in the Dark? It's not because one of them is simply a genius who keeps it all straight in his head, it's because Slaverun has a high-level system for triggering scenes, and that system is used over and over again to run the story content. This allows the story content to be tested simply by triggering those scenes at the high level.
In contrast, every story segment in TitD and DP is a hand-crafted snowflake, built out of quest stages, gated dialog, forcegreets, and AI behaviors.
Anything that uses Skyrim AI is inherently fragile. It's mostly fine when you have it working right, but only mostly, and it's hard to get right in the first place. AI sometimes fails for no obvious reason, and the CK mechanism for editing it is extremely fragile. Edit working AI, change nothing, and it suddenly stops and will never run again. At all. As for sharing AI ... it's hazardous.
Scenes require no AI behaviors. All you have to do is ensure the participants are present, then start the scene.
If the scene has the PC in it, you probably have to move the camera to a dummy and lock their controls while it's running. If you have a scene management system you only need to write that code once.
Where scenes go wrong (and alas, Devious Followers is the archetype for this) is when they bind scripting into the scene stages, ad-hoc, possibly even advancing various quests as they go.
This is terribly hard to maintain, because you have to know exactly which scene steps have script buried inside them. There are so many places you can hide a script away in a scene. For example, on the dialog lines instead of on the stage that fires them. There is too much freedom, and if you abuse it, you'll never untangle the mess that results.
If you're going to run a script on a scene, stick to one script, run at the finish. At most two (one at the start, and one at the finish). You should need very special reasons to run scripting mid scene, and if you do, it shouldn't advance quests or make changes to internal state that advance quests, it should be purely to help the NPCs "act" during the scene.
7) SexLab can fail
Don't assume that a SexLab scene will start and run to completion just because you try to start it.
If your quest progression is controlled by an animation end handler for SexLab, one missing animation blocks your quest.
This is a very simple mistake, and it's simple to fix. Never advance quests (directly) based on a SexLab animation end.
If you want to run a pre-defined SexLab scene - create a high-level manager to ensure that shared logic for SexLab scenes is written once and tested many times, not different and unique for each use case.
Design the high-level manager so it ensures all participants are present before trying to start the scene, starts the expected scene, and returns, or raises a mod event on animation end. If you know the animation failed to start, handle that immediately in the manager (spit out a Debug.Notify and act as if it ran). In the event of no animation end, use a timeout to catch it. No matter what goes wrong, when the manager determines the scene ended cleanly, or didn't start, or crashed, the caller can safely advance the quest.
If there's a debug notification that the player can see in the event the scene doesn't run properly, all the better.
If the manager crashes and takes down the control flow, you're still hosed. You can defend against this, but it adds complexity.
A high-level scene manager that ensures participant presence solves a lot of problems. I'd rather have an NPC appear from nowhere for a scene, than the entire quest sequence block forever because they weren't in the right place at the magic moment.
In most cases, a well written manager will not crash, even with missing animations - that should be a tested case.
Deadly Pleasures requires the player to have a special "Patreon contributors only" animation pack. It says it works with the free version, but that's not true at all. Because it doesn't handle missing animations well, and because using the free animation pack leads to many missing animations, this results in broken quest progression on numerous occasions.
I would suggest that this is not a good development pattern, unless you want players to start avoiding your mods due to a consistent expectation of nothing but broken quests.
8 ) Branching within Quests
Skyrim quests, as designed by Bethesda, are not really intended to support branching within a quest.
If you look at vanilla quests, they rarely introduce optional content. If they do it's usually a stage you can progress by either method (a), or method (b), or a stage with an optional step (c). Most quests progress with simple AND conditions. If you meet all the requirements, you go on to the next major stage.
The flow of vanilla quests is typically sequential, (often) with no stages skipped, and stages only advancing. Some quests let you skip a stage here or there, or provide a basic OR in how you can progress. When they want to start a genuine branch, they usually launch a new quest to do it.
The quest system is designed to support this model, and becomes awkward if used for other models.
Quest stages can only get set to higher values. Stages have a concept of completion. Code is run on stage start, and on completion do nothing other than a SetStage().
Usually, new stages clean up the objectives of old ones. It's quite simplistic.
While we have a function that is run on stage entry, there is no equivalent for stage exit (which is a serious flaw, and is probably why so many sloppily made quests don't clean-up objectives properly).
There's an interesting bug/feature here ... if you SetStage to a LOWER stage number, the set will not change the stage, but the stage entry script will still execute! This can lead to modder confusion.
You can definitely create more sophisticated logic, but Skyrim does nothing special to help you. Quests are designed to work the simple way I suggest above, and that's it.
That is how the journal objective system is designed to work: do steps, in order, finish quest, success, get reward.
There aren't many branches in Skyrim vanilla. There are some exclusive choices, but they're usually done by starting two quests and failing one of them when you finish the other. Civil War does this for Storm Cloaks vs Imperials vs Broker Peace.
Using that model, it can launch a new quest any time, and have that quest shut-down competing branches.
If you want to make vanilla-style quests, you should design like this. It's not a huge benefit, but it's a benefit. The alternative is that you have to write a quest system of your own in script.
Misapprehension of this pattern leads to Delzaron coming unstuck sometimes, but most of his quests are linear and fit this model perfectly.
Fish seems to "get" it, but still gets sloppy with objective clean-up.
Sex Slaves permits branching, so it launches "competing" quests, but you can't complete them all successfully.
While you can build multiple paths into a single quest, it's not really the intended pattern and it makes objective clean-up harder; there are ways to be rigorous about it, but Beth just don't bother; it's probably easier and cleaner to make a new quest.
e.g.
10 Learn you must find the maguffin
20 Magguffin found - sets objectives "give maguffin to a Ulfric or Balgruuf"
30 Maguffin given to Balgruuf - start new quest Balgruuf's Maguffin
40 Maguffin given to Ulfric - start new quest Ulfric's Maguffin
50 Clean up objectives ... Completion
vs
10 Learn you must find the maguffin
20 Magguffin found - sets objectives "give maguffin to a Ulfric or Balgruuf"
30 Maguffin given to Balgruuf - go to stage 100
40 Maguffin given to Ulfric - go to stage 200
100 Clean up common objectives from 10-40
110 Do stuff with Balguuf's maguffin... Go to stage 300
... (various stages)
150 Complete Balgruff's maguffin quest-line
200 Clean up common objectives from 10-40
210 Do stuff with Ulfric's maguffin...
... (various stages)
260 Complete Ulfric's maguffin... Go to stage 300
300 Completion
And if you want to unify clean-up ...
10 Learn you must find the maguffin
20 Magguffin found - sets objectives "give maguffin to a Ulfric or Balgruuf"
30 Maguffin given to Balgruuf - set Balgruuf flag - go to stage 50
40 Maguffin given to Ulfric - clear Balgruuf flag - go to stage 50
50 Clean up objectives from before (unified clean-up), branch on Balgruuf flag, go to stage if true 100 or 200 if false
100 Do stuff with Balguuf's maguffin...
... (various stages)
150 Complete Balgruff's maguffin quest-line... Clean up Balgruuf objectives ... Go to stage 300
200 Do stuff with Ulfric's maguffin...
... (various stages)
260 Complete Ulfric's maguffin... Clean up Ulfric objectives ... Go to stage 300
300 Clean up common objectives ... Completion
See how much simpler it is if you don't do branches within the quest?
Often, we'll see quest authors mush their stages together, performing multiple activities per stage to get around this.
This muddy thinking often leads to them getting muddled about what a stage should do, or having poor stage planning and design.
It also leads to "objective clean up in completion code" which then creates broken objectives when players SetStage from the console.
Last Words
A smart person learns from their mistakes. A really smart person learns from the mistakes of others.
I know this seems like a hatchet job on poor Delzaron. I used his mods as an example, but he's not alone in doing this. I love what he's trying to do. I admire his persistence. His mods are consistently interesting, and raise all kinds of provoking possibilities. However, his scripting is a tail of tears, and I think it's worth examining why, so that others don't go down that same road and fall straight into the same potholes.
There are other good/bad habits I didn't mention.
Maybe you've come across some quest patterns you'd like to never see again?
9 Comments
Recommended Comments