![]() |
A Zombie Horde approaches! |
AI Packages
Packages are sets of instructions that influence the behavior of actors in the game. You can think of them as configurable AI that gives actors (like zombies) a list of possible actions to perform.Many actors in the game, including Feral Ghouls, have default packages assigned. One in particular has a lot of useful functionality already called the Default Master Package which I'll talk about below.
Linked References
Step one is figuring out how to make them move. It really isn't a horde if the zombies just stand around. What I wanted to accomplish was zombies that walk around aimlessly if they are idle (not in combat).
This is where the aforementioned packages come in. The Default Master Package has procedures for Follow and Patrol. Both of these procedures work by making the actor path to a linked reference. A linked reference can be just about anything and there are methods in Papyrus to GetLinkedRef and SetLinkedRef.
Patrol (aka. make them wander)
The patrol package is triggered if a zombie has a linked ref set to something like an Idle_Marker. Idle Markers are hidden objects that mark a particular location or object. They can be explicitly assigned as a linked reference to an actor or idle actors can "discover" them on their own if they happen to be in the area (like settlement workshops).
In my case, I spawn an Idle Marker in a random direction away from a zombie I want to make walk and then assign the marker as the zombie's linked reference. This is enough to make the zombie slowly walk (regardless of the Speed setting on the holotape).
Once they get to the marker, they stand around. Of course I want them to continue wandering aimlessly until they bump into something (ie. enter combat). To do this, I use the RegisterForDistanceLessThanEvent between the zombie and the Idle Marker. This event triggers when the zombie gets within a certain radius I define of the marker. When it triggers, I simply move the marker. This pattern repeats and the zombie continues to follow the continuously moving marker.
The direction they move is random at first and then they generally stay in the same general direction afterward. I assigned a number to the basic cardinal directions and used an Actor Value assigned to the marker to keep track of the direction. The first move is completely random, then the subsequent moves can be straight ahead (no change) or 1 value deviation from the last direction.
7 (NW) | 0 (N) | 1 (NE) |
6 (W) | 2 (E) | |
5 (SW) | 4 (S) | 3 (SE) |
Dealing With Patrol Problems
Unfortunately, there are times when a zombie is unable to reach the Idle Marker or at least get close enough to trigger the DistanceLessThan event. In those cases, the zombie will just stand around and stop moving so we need to do something about it.
I created another Actor Value for timestamp and assigned it to the marker to keep track of the last modified time. This timestamp is set when the marker is created and any time it is moved.
I already have a hidden quest with a trigger to do some processing every 10 seconds or so. I updated this function to add a check for all markers (of a type that I spawned). If a marker's timestamp is older than some threshold I define then the marker is deleted and I send a Custom Event.
All zombies that are spawned listen for my custom ZombieMarkerDeleted event that is generated by my quest's script and respond to it. In the case of a zombie that is wandering, it begins the wandering process all over again by spawning a brand new marker in a random direction.
I created another Actor Value for timestamp and assigned it to the marker to keep track of the last modified time. This timestamp is set when the marker is created and any time it is moved.
I already have a hidden quest with a trigger to do some processing every 10 seconds or so. I updated this function to add a check for all markers (of a type that I spawned). If a marker's timestamp is older than some threshold I define then the marker is deleted and I send a Custom Event.
All zombies that are spawned listen for my custom ZombieMarkerDeleted event that is generated by my quest's script and respond to it. In the case of a zombie that is wandering, it begins the wandering process all over again by spawning a brand new marker in a random direction.
From Wandering Zombies to a Horde
OK, so at this point we have every zombie wandering in it's own direction randomly. To make it a horde they really need to band together some how and move in groups.
To accomplish this, I took advantage of the Follow procedure which works similar to the Patrol above. If you set a linked reference for a zombie to another zombie then it will follow it. It's really that simple.
But wait, who should follow who? I came up with a simple solution to allow the zombies to determine whether to follow or start patrolling themselves. The zombie effect script that gets attached to them (see Dynamic Script Attachment) now sets their FeralGhoulFaction rank randomly from 0 to 100 and that rank is used to determine who follows who.
- Check for nearby zombies and follow the first one you come across with a higher rank than my own.
- If no zombies are nearby with a higher rank, then start to patrol (wander by spawning an Idle Marker to path to).
- Note that any zombies that have decided to follow me will therefor also start to patrol behind me.
Dealing With Follow Problems
The only real problem you run into when following another zombie is what to do if that zombie dies. The easy solution for this is to register for the OnDying event and follow someone else (or start to patrol ourselves).
Issues With Quests
After release, a bug was reported where a player was sent to clear zombies from an area but when they got there the zombies were nowhere to be found. As you might have guessed, the horde behavior caused them to wander out of the area before the player got there.
To address this and avoid interrupting quests, I decided to disable the horde behavior for certain zombies so that they would be there when expected. To determine which zombies to not move, I ended up discovering by some research that many of the quest related actors in the game are linked to their location with a Location Ref Type. There types allow quests to quickly determine if all actors in a group are still alive (eg. functions like GetRefTypeAliveCount). You can check if any actor is used as a Location Ref Type pretty easily using GetLocRefTypes.
Another exclusion I ended up adding is for zombies that spawn in a Clearable or Dungeon location. These locations often rely on the actors being present to be "cleared" and can easily be checked by GetCurrentLocation and HasKeyword.
- do NOT follow or patrol if:
- Actor (or replacement) has a least one linked Location Ref Type
- Actor (or replacement) spawned in a Clearable or Dungeon location.
The "or replacement" refers to the optional Spawn Replacements holotape settings. If used, the replacement is taken into account in determining mobility. Note that if replacement levels larger than one for one (eg. two for one) are used, then the "extra" zombies are not tied to the original actor in any way and can and will wander freely.
Wrap Up
The combination of patrolling and following described in this article gives zombies an initial wandering horde behavior that is much preferred over just standing around. It adds a dynamic unpredictability to every play-through as well in that you'll never know where zombies might appear even if you've memorized all the spawn points.
I'll likely continue tweaking this behavior as bugs are reported and ideas come to me. I already have thoughts about adding additional marker types for noise and possible dead bodies. Noise markers could serve to attract zombies from farther away than the game's default detection range (but not make them hostile, just make them lumber toward the sounds in the distance). Dead bodies could attract nearby zombies for a feast as Idle Markers can have Idle Animations associated with them (eg. the Feral Ghoul crouching and eating animations).
0 comments: