In my last post, I talked about the “Place” quest resource and merging map-level data down to block-level. The next step from here is to propagate this information to scene-level where the player ultimately lives. I decided to tackle this along with a start on Daggerfall’s interaction modes, i.e. Steal, Grab, Info, Talk which are by default bound to keys F1-F4. What I’m most interested in here is the ability to perform an info click directly on a building to discover its name.
First step was to quickly implement the mode switch itself. Key-kinds were already in place inside the InputManager class, all I needed were a few more lines of code to track which mode player was in and a new HUD label to present switch to user. After several minutes it was possible to flip between the 4 interaction modes.
Next, I needed a way of detecting when player clicks on a building. I already track player clicks in PlayerActivate class, and can detect clicks on static scenery and doors, but not on the building model itself. I go to work looking for an efficient way to manage this. Each Daggerfall model contains a radius value stating the overall size from its centre point. This seemed like a good place to start, so I overlay the radius as a sphere collider on test buildings.
Keep in mind that I’m not storing individual building models at scene level, the entire block of buildings above is combined into one large model. This is more efficient for rendering and results in fewer draw calls to the graphics card. So I don’t know which building player has clicked on – I just know they’ve clicked a combined block model and I need to resolve that back to a specific building. The sphere colliders would do the job, but they unfortunately suffer from a lot of overlap. Depending on where player is standing and where they clicked on the building, it’s possible for that target point to be inside two or three different building spheres. There are ways of handling this of course, but I decided to go with box colliders instead that will tightly wrap Daggerfall’s little box houses.
Before I can implement boxes, I had to go right back to the procedural mesh builders that extract data from ARCH3D.BSA (Daggerfall’s 3D model archive). During the vertex conversion process, I added some tracking for minimum and maximum vertex positions from origin to get the overall size of models in X, Y, Z space. Because the models are being constructed procedurally, and I have to process that information anyway, this step adds almost no overhead to the job of loading models at runtime. I now have what I need to construct a tight bounding box around any model effectively for free.
Then I just had to pass that information to scene builders. The end result looks like this.
Every building now has a nice crisp bounding box trigger collider. While this is great to visualise the bounds, I’m not happy with adding so many GameObjects to scene. A large city like Daggerfall will add around 800+ new collider GameObjects. If a few large cities are in the world at once, then it’s very possible that 2000-3000 colliders would be added to scene, the majority of which will never be used. Not ideal from an optimisation point of view.
To work around this, I used the same solution I came up with for doors. I store the box information for every building on the block GameObject itself using component DaggerfallStaticBuildings. This is just a small amount of raw data stored on each block. When the player clicks on a block in scene level, a single box trigger is instantiated and tested for each of the buildings in that block, usually no more than 5-14 buildings. The world-space point of impact for hit ray is tested against known buildings in that block (and only those buildings). If a hit point intersects with a known building trigger in world space, then everything we need to know about that building from scene layout time is returned to PlayerActivate.
The end result is zero new collider objects in the scene and only a small amount of overhead for storing building data and testing hits, which happens only when the player clicks on a city block. The net result has practically no performance impact and still fits nicely hand in glove with Unity’s physics setup. With all of that in place, player can finally make info clicks on buildings in scene.
While I was working on this, I decided to add shop quality text. When the player enters a shop in Daggerfall, the overall quality level is presented to player as a popup. Each shop has a quality value between 1 and 20 with a total of 5 different quality popups. I’ve long assumed it was just qualityValue/4 to get the popup, but testing proved otherwise. Daggerfall weights the quality text a bit more in the mid-range that the extreme low or high ends. After checking shops in Daggerfall city and Gothway Garden, I think I have the weightings worked out. Anyway, It’s something that can be easily tweaked later if needed. Now when player enters a shop, the quality text will popup like in classic.
Because the popup actually prevents player from entering building until they click a second time, I added a quick INI option to present quality text to HUD instead, with a configurable delay. With this option configured in INI (ShopQualityPresentation=1), you can enter every building with a single click and watch the quality text scroll off HUD. You can also set ShopQualityPresentation=2 just to turn off quality text completely.
While I was on a roll with the whole info setup, I made a start on identifying static NPCs. These are the flat people standing around inside buildings and interior environments like Daggerfall Castle. This will be critical to dialogue as part of questing system eventually, so might as well break some ground for later. The first part of this was simple, just detecting when player clicks on an NPC.
Yep, that’s an NPC alright. Exactly how an NPC is detected had to evolve a little over the process of getting this initial text in place. I previously did this by detecting if the billboard had a faction and a gender – something I assumed only NPC billboards would have. This turned out not to be the case and there are plenty of factionless NPCs standing around. Also, the NPCs in building interiors had a different metadata setup to NPCs in dungeons. Just like Daggerfall to use two different data structures for effectively similar things. I ultimately determined NPC status by the texture archive flat belonged to. There are a small range of NPC-specific texture archives and this served better than faction to determine if player has clicked an NPC or not. In the process of doing all this, I actually gained some better understanding of a particular bitfield and found a new gender bit assigned to NPC records.
Before I can identify an NPC properly, I need to use their faction information. An NPC with faction type=4 is an individual NPC which means their name comes directly from faction data. Other NPCs just get a random name generated based on their gender. So before diving any farther down this rabbit hole, I have to implement factions for the player.
I had written faction file reader around a year ago, but this just reads the initial database from FACTION.TXT in game data. In Daggerfall, this initial faction data is assigned to player character at time of creation and persists with player all through their adventuring career. Every interaction can raise or lower their standing with a particular group. To start tracking faction data properly, and to make this information available to NPC info clicks, I added the PersistentFactionData class to player entity. This class represents the player’s current standing with all factions and provides basic lookup functionality. This has already been wired up to save/load system, so when you save a game in future builds, your faction data will be saved as well. This is also an important milestone for the questing system.
With faction data on the player, we can identify individual NPCs like King Gothryd and Queen Aubk-i.
But it means that group-based NPCs don’t resolve properly by faction alone.
These NPCs need to have a random name based on gender. Thankfully I’ve already written the random name generators, as these were required for building names and elsewhere. The only thing I’m missing is the correct seed value to generate the exact NPC name that Daggerfall uses. For anyone unfamiliar with random generators, the same seed value will always output the same result. Unfortunately, I wasn’t able to find the seed value Daggerfall was using to name NPCs. You’d think this would be obvious like it was for buildings, but sadly not. I decided that one random name is probably as good as any other for these non-essential NPCs and just used their record offset as a seed value. This means they will always have the same persistent name generated each time, it just won’t be the same name as in classic. If the correct seed value is ever located, I can just feed this into the generator instead and the correct name will pop out. Until then, it probably doesn’t really matter. Say hello to random NPC Alabyval Coppersmith.
At the end of this little coding adventure, all of the following has fallen into place:
- Change activation mode between Steal, Grab, Info, Talk.
- Identify buildings and static NPCs.
- Shop quality text is now displayed when entering a shop.
- Faction data now exists on player entity and persists with your save games.
- The foundation for checking NPCs in scene are ready for “Person” quest resource.
- The raw data needed for “Place” quest resource now in scene.
With all that done, I can go back to working on “Place” resource and “pc at” condition for quest system. You can see what a rabbit-hole this whole quest setup is. It touches on so many different parts of gameplay that sometimes I need to go in a different direction for a while before I can loop back and progress on what I’m trying to build. But it all adds up, and every tiny step brings us closer to real gameplay.
For more frequent updates on Daggerfall Unity, follow me on Twitter @gav_clayton.