When you enter a zombie apocalypse themed world, you expect to see lots of zombies. To that end, I wanted to provide the option to replace several of the built-in creature types in Fallout 4 with zombies.
Leveled Lists
Fallout 4 uses leveled lists for several things including creature spawns. Not all actors in the game world are spawned this way, but most of them are which make them an excellent way to put more zombies in the world.
 |
Bloodbug Leveled Character List |
Basically, leveled lists give the game a way to "spawn a bloodbug appropriate for the player's current level". The bloodbugs with higher number in the example above are stronger and higher in level. Once the player hits level 10, the second variation of bloodbug (EncBloodbug02) will start to spawn.
These leveled lists can be changed by mods and completely different creatures can be added to them. So when the game says "spawn a bloodbug appropriate for the player's current level", a Zombie or Alien or Gorilla etc. might appear instead.
Option 1: modifying leveled lists directly (bad)
These leveled lists can be directly modified by drag & drop. You can both drag existing entries out of the leveled list (removing them) or drag new actor types into the leveled list (adding them). You can also adjust the levels associated with the entries in the list. Doing this allows you to completely replace 100% of leveled list spawns with another creature type if you want to.
Doing this will mark the file as included by your mod but there's a big problem with this. If any other mods modify the same leveled list file, then your mod will conflict. In scenarios where two or more mods modify the same file, the load order comes into play. The last mod loaded wins. You can think of it as each mod effectively overwriting the file changes of previous mods.
For most mods, you can't avoid all possible conflicts as you have to edit some files. That is sort of the point of a mod. But in cases of leveled lists (for spawns, weapon drops, etc.) it is best to avoid modifying them as they tend to be touched a lot by mods. As such, there's no sense in adding possible conflicts when there's another option available...
Option 2: modifying leveled list with a separate mod (a pain)
Other mod authors have created separate mods just for spawn replacements. These mods serve as sort of plugins to the main mod and only focus on replacing a single type. This has the advantage of allowing them to replace 100% of leveled list spawns and only be applied if a user chooses to download and enable that extra mod.
However, the big disadvantage is that mod authors have to maintain multiple mods and end users have to download and enable multiple mods. Keeping everything in one mod is cleaner and simpler. It also makes it a lot easier to enable/disable zombie mode when switching between characters as there is only one mod to toggle. This approach still has the disadvantage of possibly conflicting with other mods as well.
Option 3: modifying leveled lists through scripting
Fortunately there are functions available to modify the various leveled lists in Fallout 4. For spawns there is a
LeveledActor script with the following methods:
- Function AddForm(Form apForm, int auiLevel)
- Adds the given form to the leveled list under the given level.
- Function Revert()
- Removes all script added forms from the leveled list.
This gives you the ability to add and remove all, but I really wish there was a RemoveForm method to remove specific entries. Why? Well, a couple reasons. First, I have no way to remove the existing entries in a leveled list so I can add a bunch of zombies to the list but they'll never spawn 100% of the time. Second, the Revert method can potentially remove entries added by other mods.
Since this is all I have to work with, I tried to make the best of it and provided the following options:
- Disable (calls Revert and effectively removes any Zombies that may have previously been added)
- 50% (calls AddForm once per entry in the list)
- 75% (calls AddForm three times per entry in the list)
- 90% (calls AddForm nine times per entry in the list)
An important thing to understand is that when there is more than one entry in a leveled list for a given player level, the game will randomly select one of them. So for each level I have accomplished a % chance that a zombie will spawn instead of the default type by adding the appropriate number of zombies for each level in the list.
- 1 bloodbug, 1 zombie = 50% a zombie is selected
- 1 bloodbug, 3 zombies = 75% a zombie is selected
- 1 bloodbug, 9 zombies = 90% a zombie is selected
Leveled list properties
Leveled lists have a couple properties that can help in figuring out how to approach 100% replacement:
- Calculate from all levels <= PC's level - if checked, when randomly choosing an actor to spawn all actors in the leveled list less than or equal to the players level are available. This means leveled list entries marked at level 1 are always possible to spawn regardless of player level. If not checked, then only actors at the highest level less than or equal to the players level are available.
- Calculate for each item in count - if checked, the choice of which actor to spawn is made ONCE and all actors that spawn will be that same actor type/level. If not checked, then actors are randomly selected each time so groups that spawn may be of mixed type/level.
 |
Bloatfly leveled list with both properties not checked. |
Consider the bloatfly leveled list example above. The "Calculate from all levels <= PC's level" property is not checked. If we add 9 zombies at level 1, then when the player is level 1 there is a 90% chance for a zombie to spawn. If we add a single zombie at level 2, then when the player is level 2 there is a 100% chance a zombie will spawn. This 100% chance will remain true as the player levels until they hit level 9.
Level 1, 9, 17, 27 = 90% zombie spawn (assuming 9 zombies added for each of these levels)
Level 2-8, 10-16, 18-26, 28+ = 100% zombie spawn (assuming 1 zombie added at levels 2,10,18,28)
If you do the math for bloatflies up to player level 30, then you are looking at an overall replacement rate of around 98.7%.
Implementation with global functions and properties
In implementing the holotape I realized I'd be modifying a lot of leveled lists in a similar way. I wanted to create some helper functions to make my life easier. After some research, I realized I could add a general script to each holotape menu that had global functions defined in it. Inside the Papyrus Fragment for each holotape menu item I can reference those global functions. This basically limited the amount of code I had to write for each replacement to one line per list per fragment.
 |
Replace Bugs terminal config showing global property script and fragment using it. |
Using the global function everywhere made my Papyrus Fragments basically simple scripts with a whole lot of properties. I configure properties for each leveled list to be modified and properties specifying the lists of zombies to add to each of those lists. Since the LeveledActor script does not have any "Get" methods, I have no way of knowing by scripting what levels are mapped. Therefore, I created additional properties for each leveled list to specify an array of levels so I know at what levels to add the zombies at.
 |
Replace Bugs Papyrus Fragment Properties |
Bug: headless zombies?
For each leveled list I am updating I tried to add the appropriate matching zombies types. Normal zombies for normal lists, legendary zombies for legendary lists, etc. However, I ran into a bug with the ambush list types.
I went to Red Rocket, greeted Dogmeat, and then waited for the inevitable molerat ambush. Once the enemies spawned, the zombies had invisible heads. They still moved and attacked as normal and I could shoot them where there head should be but the heads were simply missing.
As a result I decided to replace the ambush lists with normal zombies instead until I can figure out what is going on. You'll notice the properties screenshot above has both ZombieList and ZombieAmbushList mapped to the same FormList (ZombieList).
Edit: this is still a bug with the normal ZombieList that I may blog about in a separate post.
Summary of advantages/disadvantages
Using scripting allowed me to make the replacements configurable via holotape and greatly reduce the chances of other mod conflicts. It also keeps everything contained in a single mod which is easier to use and maintain.
However, due to the lack of a RemoveForm function I was not able to offer a 100% replacement option and my mod still runs the risk of removing other mod added spawn options if users choose to disable replacements after adding them.
Update 11/24/2016
0 comments: