Magic & Effects Back-End
Posted on
March 28, 2018 by
Interkarma
In my previous article, I showed progress on the visual side of spell-slinging and had lots of fun with casting animations and throwing around missiles with lighting effects. Now I have to regard the business end of the spell system and how all of this holds together under the hood. This article will be a lot more tech-oriented than my previous one, but may still be of interest if you’re curious about how spells will operate in Dagerfall Unity.
Please keep in mind this is all under active development so concepts discussed here are likely to be refined or expanded by the time everything rolls out.
Magic & Effects System
One major shift in this process was changing how I think about the spell system. I have a long list of goals I want to achieve during this stage of development, above and beyond just emulating Daggerfall’s classic roster of spells. Primarily, I want to create a central way of handling the majority of effect-based gameplay. This means advantages/disadvantages, diseases, poisons, spells, magic items, potions, and so on should all come together under the one system or group of related systems. Once I had made that decision, it no longer made sense to call it the “Spell System” as spells are just one part of the collective. So the
Magic & Effects System was born.
This is why you won’t see the word “spell” very much moving forward but you will see the word “effect” a lot. In this context an effect isn’t something visual, it’s how something
works. For example, an effect that heals the player is a script which increases their current health. This naming is taken from Daggerfall itself where spells and magic items reference effects directly using a type and sub-type. You can read more about classic Daggerfall’s spells and their effect indices on
this UESP page.
You will also see the term effect used by Daggerfall’s Spell Maker UI when creating a new spell. You can add up to three effects per spell as shown in screenshot below from classic Daggerfall (spell maker is not yet implemented in Daggerfall Unity).
Overview
If you’re familiar with the Quest System and how registered Quest Actions execute inside of a Quest – then you’re already familiar with the Magic & Effects System. I’m iterating on some of the concepts for Magic & Effects, but the broad design remains very similar. There are a couple good reasons for this: I want a consistent approach so developers can apply learning from one system to another, and both systems do something very similar which is execute a script inside a framework of some kind to perform work for the game. Here’s a nifty overview of the classes so far before I break it down further:
EntityEffectBroker
The EntityEffectBroker singleton lives within the game scene at all times, and will be involved any time a new effect enters the game. It fills a similar niche to the QuestMachine in that it helps coordinate everything and provides a common entry point for execution. At startup it uses reflection to enumerate all classes implementing IEntityEffect inside the main assembly and registers them using an “effect key” presented by the effect itself. Eventually, mods will be able to inject new effects at startup without needing to rely on the reflective enumeration.
DaggerfallEntityBehaviour
This MonoBehaviour-derived class has existed for a long time but deserves a revisit to explain how it fits into Magic & Effects. Everything that walks, crawls, flies, or swims in Daggerfall is considered an entity and carries a DaggerfallEntityBehaviour component. This in turn holds the DaggerfallEntity instance which defines their career (class / monster type) and all of their stats, skills, health, spellpoints, etc. Most effects will operate directly upon an entity in some manner, which is why the EntityEffectManager is peered with this component.
EntityEffectManager
This component stores effect collections (called bundles) that are currently operating on the entity. At runtime, it will step through all effect bundles assigned to the entity and execute them. It will also remove bundles when they have expired. Generally speaking, this class is responsible for handling the lifetime, execution, and persistence of effect bundles related to a single entity.
EntityEffectBundle
The aforementioned bundles come to life through the EntityEffectBundle class. This is both a collection of effects and an execution container for them. A spell is just one example of a bundle – spells have one to three effects that share a common duration, chance, magnitude, magic type, etc. Every effect that exists in the game will be contained by an EntityEffectBundle. It’s similar to a Quest object in that effect bundles execute child effect scripts in a similar manner to how Quests execute child action scripts. Besides all of that, effect bundles are used to
transport effects from one place to another, for example via a spell missile. The variety and management of effect bundles will see heavy work over time.
IEntityEffect
This is an interface to the effect. An interface is like a contract that defines what properties, methods, etc. a class must implement. The EntityEffectBundle will store a collection of effects using their IEntityEffect reference.
BaseEntityEffect
A default abstract implementation of the effect interface used to reduce workload by effect authors. The base class handles most of the common boiler-plate for any effect.
Effect
The effect itself. This is the script which actually performs
work against the world, target entity, or whatever else the effect is designed to do. Just like a QuestAction, an Effect class is C# code executed by the owning EntityEffectBundle, which in turn is executed by the parent EntityEffectManager. Each effect is enumerated, keyed, and later factoried by the EntityEffectBroker when being added to a bundle. The effect itself should be very atomic, performing only the job it was intended to do. Effects will be insulated from the gory details of the wider systems and only need to take care of their own execution and persistence.
DaggerfallMissile
This is a mobile object like a fireball the player shoots into the world. Missiles aren’t just a visual thing, they are also used to carry effect bundles from one place to another, such as the fireball mentioned a moment ago. However, the appearance of the missile will depend on the magic type property of the effect bundle it carries (e.g. frost, fire, poison). Any entity hit by the missile will find the effect bundle payload added to their EntityEffectManager and they will suffer the consequences.
FPSSpellCasting
This is purely the visual side of casting spells for the player. It is the glowing hand attack you saw in my previous article. It deserves a mention because just like missiles, the casting animation will be selected based on the magic type the player is using (e.g. frost, fire, poison).
Immediate Mode Effects
Now you have a high-level overview of the major classes making up the Magic & Effects System, but something I haven’t talked about is how execution itself operates. I liken effect system execution to an Immediate Mode GUI, in that every tick of the effect system will expect current state from every active effect. Rather than try to track what every single effect is doing (which is a logistical and concurrency nightmare) the entity and effect management classes work around this by
just not caring about the current state of any effect.
This simplifies both ends of the execution process. First of all, the EntityEffectManager does not need to track and remove state for effects operating upon its peered entity. There’s a limitless number of permutations of what an effect could do, and the ability to execute custom effects through the mod system further expands what is possible. The EntityEffectManager can’t possibly account for everything an effect might change, so it doesn’t try. It just requests every effect to apply its current work every frame. More on this in the next section.
Second, the effects themselves don’t need to worry about what other effects are doing – they only need to worry about the
work they want to perform. This work might be to increase/decrease health, or fortify an attribute, or summon an item, or open a UI, but whatever that work is the effect itself gets to manage its own execution and state with almost complete control over itself.
If that all sounds a bit complex, please don’t worry about it. Everything works out to be fairly simple in reality, and there will be dozens of effect scripts to learn from. I expect the majority of effect scripts will be no more than 20-30 lines of code.
Sandboxing Effects
I mention above that effects have almost complete control over their own state and that management classes don’t even try to track what those effects are doing to the entity. You’re probably asking at this point how changes to the entity like a damaged Strength attribute is removed once the effect is finished? This is where some minor sandboxing comes into play.
Rather than allow effects to operate directly on the entity’s stats and skills, each effect has a payload of modifiers that it can use to change these attributes while active. The first part of this went into the game some time ago. DaggerfallStats and DaggerfallSkills each have a “mods” array that hold potential changes for every value (note these aren’t game mods, that’s just what the array is called).
When requesting a stat such as Strength, the game can request either the LiveStrength value or the PermanentStrength value. The Permanent value is the actual value stored in the entity. This is never touched or altered by an effect script. The Live value is a combination of the Permanent value modified by all combined effect “mods” active on that entity.
At the start of every tick, the effect system will first clear the “mods” state for
all attributes. Then when an effect is executed, the management classes will merge any “mods” changes emitted by that effect to the entity’s resultant “mods” array. So when a formula asks for the current LiveStrength value, it will be handed the player’s real Strength plus or minus any changes made by effects. When those effects expire, the next tick of the effect system will clear its changes from the resultant set of modifiers.
This minor sandboxing process helps ensure that effects cannot accidentally alter the player’s attributes in any permanent way, provided they follow the design in place.
Not everything will be sandboxed in this manner (which is why I called it “minor” sandboxing earlier). Some values such as Health and Fatigue are designed to be raised/lowered constantly by several external factors, and are also very easily recovered. Any effects changing these values are fine to operate directly on live state.
Conclusion
The Magic & Effect System still has a very long way to go, but hopefully this article will serve to ground developers on the direction it is taking and what certain concepts mean inside the code. I hope to have active test effects with a real lifecycle running soon. This will help me further build the execution framework and start rolling out actual effects.
From there, work will progress deeper into Spellbook and Spellmaker UIs until the player is more-or-less casting real spells in the game. And by then, things should be far enough along to start adding support for magic items, diseases, advantages/disadvantages, potions, and so on.
As always, I’m looking forward to progressing beyond the design and build-out stages to actual implementation. Hopefully this will be the only dry, technical article in the series.
For more frequent updates on Daggerfall Unity, follow me on Twitter @gav_clayton.
Guild Systems
Posted on
March 21, 2018 by
Hazelnut
Hi everyone, Hazelnut here. Interkarma has asked me to write a blog post about some of the work I have been doing recently, so for this post Daggerfall Workshop has been taken over by me! Muhahahaha… etc. I’m sure your regularly scheduled Interkarma posts will return soon, so don’t worry.
Anyway, allow me to introduce myself, you may know me from such features as horse riding, inventory upgrades, shopping, persistent state, icons, tavern rooms, ships and houses, as well as various other bits and bobs. So why did I spring up from nowhere and start contributing to DFU?
I bought Daggerfall on xmas after its release and played like crazy for 7 days until the end of the christmas break, then stopped. Because it’s such a huge game, never really got back into it for years due to lack of time & small children – until shortly before Morrowind came out. By then the game was simply far too dated for me to persevere, especially because I forgot about the WSAD option and I was using the default control scheme of stupid mouse pointer arrows to move. So I’ve actually never played beyond the first couple of character levels. Always wanted to, and do intend to once DF Unity is done. This is why I am absent for any discussion or work around the main quest! Spoilers! Yep even after all this time.
While the progress has been fantastic (Interkarma has Orc level stamina apparently) it’s a big project and takes a long time, and since I’m a software engineer for the past 2+ decades who now does software design so don’t get to write much code at work… I figured I’d see if I could contribute. Also my kids are all teenagers now, so I have more free time than I’ve had for 2 decades. I had no knowledge of Daggerfall and its data structures, hardly ever touched C# and never used Unity before, so it’s been quite a learning curve. Anyway, after I finished my work on ships, houses, shops and taverns I asked Interkarma what he thought I should tackle next and between us we decided it was time to implement guilds. I’d already done some work with the guild services menus, but now I needed to create the guild membership systems.
Guilds
Guilds in Daggerfall follow some common rules for promotion etc but offer different services and quests. The first thing to focus on was ranks and building some foundations that would make each guild easy to implement, allowing the common behavior to be shared. I also wanted to ensure that new guilds could be added by mods, as I was sure that several people in the community were keen to add new guilds having seen discussions on the forums, and I had some ideas of my own that would be best integrated into the base game by adding a new guild. I decided early on that the guild code should be designed to support modding from the start, and the only way to ensure this was to actually implement a guild mod early on to prove the concept.
So, quite a lot to take on. Fortunately I had 3 solid days free coming up to get started on this. The fighters guild is the simplest of them all so I started with that. First step was to test classic and see how guild membership worked with rank changes etc. To support modding, the guild classes are designed so that they know about the players membership status, rank, and what benefits and services the guild provides at that rank. Service and guild management code asks the guild class by calling the appropriate method when they need to know. They also supply guild specific messages to the ranking system which is shared. This means that a new guild can simply be added by implementing a new guild class and registering it with the guild manager.
Now by this time you could join the Fighters guild.
Joining the Fighters
Next thing was to show the guild memberships in the affiliations section of the character sheet. This required orange text which I think isn’t used anywhere else in the game.
Affiliations
Next, I fleshed out the benefits and services being offered at each rank and integrated it with the service menu I had previously done, then moved on to detection of rank changes. For the time being this is being checked every 24 hours rather than the 28 days it’s supposed to be. Resting for a month in classic daggerfall to test rank changing was incredibly tedious, 99 hours at a time! Also, for now, you can rest in guild halls for free as soon as you join – so testing doesn’t require finding a nearby inn. To test I was modifying guild reputation in savefiles and then reloading. Once promotion, demotion and expulsion were all done and working for the fighers it was time to deal with the harder bits.
Temples
So, temples are guilds but complicated because it’s like a single guild (you can only join one Temple) in many ways and multiple guilds in others. Players get different benefits at different levels from each different Temple, and the guild system needed to be able to cope with this. Knightly Orders are similar, but a lot less complex because they don’t offer training and curing services. I decided that Temples would be a single guild, but with a Divine variable set to enable variations. Building this required changing quite a bit of what I had built for Fighters, but this was the point of tackling the most complex second. It may not sound difficult, but I found it tricky to enable temples to offer training to non-members (at a higher price) because the services system was hooked into guild membership. Solved this in a way that is not available to new guilds, but not worried because this is a unique feature of temples and is so joining one temple doesn’t prevent you from training at a different one.
Getting training when not a member
Proving the foundations
The next step was to prove the foundations were designed and build right by implementing the Mages guild. This took less than an hour, with most of the time spent finding the correct message id’s in text.rsc for joining and being promoted. The fact that it was so quick and easy let me know that I had things right. It had taken quite a lot of work, but the foundations were done.
Mages don’t want you
A New Guild
Now it was time to prove that a new guild could be built and added by a mod. This threw up quite a technical challenge – how to integrate a new guild into the game world. As you will be aware if you have followed Interkarma’s blog posts, Daggerfall Unity works off the original games data files. This means the game world is set in stone within these files. (contrary to some opinion, there is no runtime procedural generation – it was all done upfront) After much thought and looking through the DFU code which reads and parses the world data from original files, which I had not had much reason to look at before, I realised that this data could be saved to text files in JSON format by simply using the serializer that’s already used to create saved games. So, I created some console commands to dump Daggerfall block data into JSON files, with the thought that I could modify these files and have them override the original game data at runtime.
I’ll not go into too much depth here, if you are interested check out my Guild Mod tutorial thread:
http://forums.dfworkshop.net/viewtopic.php?f=22&t=901
Anyway I got to the point where a building within a block had a completely new interior. See these before and after shots.
Before the world data is overridden
After overriding the interior world data for this building
It was very successful, only requiring a couple of tweaks to the guild foundations in the core DFU codebase. Things were progressing very well, but I had to do some work with the mod code to enable all this to be packaged into a DFU mod file which took a while to figure out. I hope a lot of players find the Archaeologists guild mod a valuable addition to their DFU play experience. See my forum thread for more details:
http://forums.dfworkshop.net/viewtopic.php?f=14&t=888
Questing and Reputation
The last piece of the puzle for guild foundations was to migrate the test quest dispensing over to the guild service code and have several managed pools of quests for each guild. To do this I modified the table structure for quest lists so that each quest could be restricted to members / non-members and also by guild reputation. Then it was fairly straightforward to add all the different guild quests and have the right ones selected from when you ask for a quest. Again some more work was required to enable quests and lists to be packaged into mods, which I proved by adding a single quest to the Archaeologists guild mod. Also I created a more general mechanism for adding quests to Daggerfall Unity from Interkarma’s suggestion to allow quest packs for pure quest mods so authors didn’t have to package a full mod.
This works by scanning the full directory tree under StreamingAssets/QuestPacks for quest list files named “QuestList-whatever.txt” and loading them into the guild quest pools. Jay_H will be releasing the guild quests he wrote last year as quest packs soon, so hopefully this will demonstrate how easy it is! (or possibly what needs to be re-worked, noooo)
There’s still more to do for non-quild quests, but for now the focus is just guilds. The last thing I put in place was for quest success / failure to modify the players guild reputation – I left this until last because it was so much easier to just edit this during testing. By default each guild quest successfully completed increases your rep with the guild faction by 5, and failing decreases by 2. However, some quests shown on UESP have different reputation changes.
e.g.
http://en.uesp.net/wiki/Daggerfall:Still_More_Trouble_with_Orcs and
http://en.uesp.net/wiki/Daggerfall:More_Trouble_with_Orcs
I don’t have any idea how this is done in classic, or even if it was ever implemented – much of the info on UESP seems to come from Daggerfall Chronicles which was written from intentions before the game was complete. So if anyone out there has any info on this, or is prepared to do some testing please drop by this thread on the forums and give me a clue:
http://forums.dfworkshop.net/viewtopic.php?f=4&p=11032
So, there you have it, a brief story of my journey implementing guilds. Hope everyone enjoys them in the latest build, and please be sure to report any bugs you find!