I’ve decided to go in a slightly different direction regarding dev articles about quest system from now on. Instead of the traditional numbered sequence split into a few parts, I will post more about what I’m working on in the moment. There are two reasons for this. First, questing is a huge subject that resists being neatly split into a progressive narrative like items or streaming terrain. I often have to cross back and forth between related systems. Second, it gets a bit daunting trying to construct that narrative from such fragmented work. The end result is that I’m posting less and feeling more weight on my shoulders every time I try to construct a post. Just how do I describe to everyone I spent weeks working on research without a satisfying answer to a particular problem?
My solution is to tear the band-aid off and just post something. I’m slowly learning that many people enjoy the development process itself, including the potential for frustration and dead ends. Those outcomes aren’t particularly enjoyable for me when they happen, but maybe I can turn them into something positive and entertain my readers. This should result in more posts but they will often be shorter (which in itself might be a good thing). This post probably won’t be shorter, but it is my first in a while. 🙂
So here’s what I’ve been working through lately…
In a QBN section, Daggerfall quests defines resources that kind of glue the whole shebang together. They define places in the world, people to visit, timers, items, and enemies. Basically all the world-stuff player will contact to unroll that quest’s execution. A high-priority resource is Place, which defines locations the player must use in the quest, like a dungeon, shop, or residence. Here are some examples.
Place _mondung_ remote dungeon9
Place _house_ local house2
Place _inn_ local tavern
Place _palace_ remote palace
Place PiratesHold permanent PirateerHold1
To deconstruct the above: Place tells the compiler this is a place resource; _symbol_ is the name of that place for the compiler to reference; remote/local/permanent is where to select that place from; and the final bit is site which tells quest engine what type of place to select. The site code is actually a reference to a places data table which defines the parameters for these site types. I won’t go into these in much detail, just keep in mind they define what kind of building or dungeon to select.
Let’s use “local tavern” for now. This means find a random tavern within the same town as where player received quest. If this was “remote tavern”, we’d select a random tavern from another town in the same region. The site code “tavern” is then looked up in the places data table and the parameters tell us this is building type 15 (0x0F).
Now the quest engine knows it needs a tavern (building type 15) and it should select from the map player is currently in. Fortunately, Daggerfall keeps a handy list of buildings for every map definition in MAPS.BSA. This gives us information like the building name seed, faction, quality (“rusty relics” through “incense burning”), and best of all building type. Great! We already know we need a tavern, and it’s trivial to select taverns from this list of buildings and grab one at random. Now we have our quest location… well, almost.
A Daggerfall city is made up of geomorph-like blocks – up to 8×8 blocks for really big cities. They are like puzzle pieces laid out on a grid. Here’s the block layout for the City of Daggerfall, you might recognise this from Daggerfall Modelling.
Every block also carries a list of buildings it contains, sharing the same data structure as MAPS.BSA – with a few differences. One key difference is that a few of the values present at map level (e.g. name seed) are not present at block level. The reason for this is obvious when you consider how often blocks are reused across locations. If the name seed was stored in block data, our tavern would have the same name in every location that block is used. Thus the name seed is carried at map level and merged down to block level as needed. Same goes for the block texturing, which changes based on climate. Put together, this all makes blocks somewhat polymorphic in that they can take different forms depending on the location they belong to.
Back to our tavern which we found in building list at map level, how then do we map this to the actual physical building at block level? Besides other jobs like setting the correct names on automap, this is needed to determine correct physical location of quest buildings in the world. This is where some of my troubles started.
At time of writing, it is unknown exactly how Daggerfall links building data at map level down to building data at block level. To compound my problems, only a subset of buildings are represented at map level. A large city like Daggerfall will have many more physical buildings than listed for that location in MAPS.BSA. Even in small locations where the totals match, the individual building type counts don’t always match. For example, map data might define 6x House1 and 4x House2, while block data will contain 5x House1 and 5x House2. There doesn’t seem to be any consistent way to link these two sets of data together. If this were a relational database, I would expect a stable primary and foreign key matching on both sides. But not only does a key not seem to exist between them, the totals cannot be trusted to line up between map and block buildings.
Despite this, I have been able to make some good progress. I discovered that named buildings (taverns, stores, guilds, etc.) are always in the same order in the map building data as the block layout data. Couple this with the building name seed discussed a while back, it’s possible to merge down named buildings without much trouble. This is also needed for the automap like below. Credit goes to Nystul for the excellent automap implementation.
When it comes to generic premises like House1/House2, things become more difficult. I can’t depend on an exact match between building type distribution, and can’t find a way these could possibly be linked in a stable manner. Both Lypyl and myself have been through the binary data in great detail. Despite finding a few other interesting leads, we have not found a satisfying answer to the question of mapping non-named buildings from map level to block level.
After many frustrating weeks of searching, I’ve more or less come to the conclusion that it really doesn’t matter. The only buildings that need to be mapped 1:1 are named buildings, and I already have a working solution for this. When it comes to generic premises (which are selected randomly anyway) it should not matter which House1 or House2, etc. Daggerfall Unity selects for a quest. As long as the end behaviour closely matches that of Daggerfall by selecting a building of the correct type, I’m basically free to implement this in a manner suitable for Daggerfall Unity.
In hindsight, the apparent lack of stable linking between map and block data could account for some quirks in Daggerfall itself. For example, it’s not uncommon to find a tavern which Daggerfall thinks is a residence. And only about half the buildings in most towns can be entered anyway (“this house has nothing of value”). My feeling now is that when a location is loaded, Daggerfall simply distributes map building data amongst block buildings using some kind of algorithm where named buildings are populated first and generic buildings are distributed to best effort. As long as named buildings are handled (they are), and other buildings are selected randomly (they seem to be), then I can at least reproduce this behaviour in a way that feels exactly the same to the player. My solution might not be exactly what Daggerfall is doing, but it’s likely very close.
So that’s where I’m at right now – working through implementation of Place resource, selecting locations in the world, and ensuring this maps to physical environment correctly. During this process, I’ve added some fun bits along the way such as the flavour text when entering a dungeon area outdoors. This text is based on dungeon type and reads from strings in Daggerfall’s text database using the same variations.
The other side of a Place resource is the “pc at” condition which tests if player is at a Place or not. This is highly intertwined with Place resource handling so I’m likely to bounce back and forth between these two for a while until I’m happy.
That’s all for now. Rather than go through a long delay between posts again, I’ll try to post as regularly as possible with updates on what I’m working on at the time. Even if the post is short, I’ll try to put in some details of the development process for you to read.
If you enjoy this sort of post, please let me know! Feedback helps me work out what people enjoy reading and hopefully write better posts in the future. Thank you for reading, everyone. 🙂