The AI has a weighting system to determine how to pick the best available target. The weights are set up in a structure in one of our script files. In the game all potential targets are evaluated according to these weights, and the target with the highest weight will be chosen. Here's what the default settings look like, with some comments on the basic parameters and what they mean:
:min-distance 0.0 ;; minimum distance to look for a target
:max-distance 200.0 ;; maximum distance to look for a target
:distance-weight 50.0 ;; weight to apply, scaled according to distance away
:target-eval-rate 1.0 ;; how often to evaluate targets, in seconds
:visibility-weight 10.0 ;; weight applied if the target is visible
:sticky-factor 10.0 ;; weight applied to the existing target
:player-weight 10.0 ;; weight applied if the target is the player
:last-shot-me-weight 20.0 ;; weight applied if the target shot me
:targeting-me-weight 0.0 ;; weight applied if the target is targeting me
:close-range-distance 11.0 ;; weight applied if target is within the specified distance
:relative-distance-weight 10.0 ;; explained later
:relative-dog-pile-weight -1.0 ;; explained later
;; weights for specific vulnerability states, explained later
:in-melee-bonus 0.0 ;; weight applied if target is in melee
:preferred-target-weight 100.0 ;; explained later
The majority of the AI in the game use this default set of targeting parameters. What these settings translate to in simple terms would be as such:
They will look for targets up to 200 meters out, preferring targets that are closer. They will also prefer targets that they can see (visibility-weight), are shooting at them (last-shot-me-weight), and are the player (player-weight). They will also favor a target that they were already targeting (sticky-factor), and will re-evaluate if they should pick a different target every second (target-eval-rate).
There are a few parameters that we can use to help distribute targeting of multiple enemies. As an example, when a larger battle is going on between multiple factions, we don't want everybody targeting the same guy.
The relative-dog-pile-weight is applied based on how many other AI on the same team are targeting a particular enemy (hence why this is a negative value). The value specified is multiplied per each team member that is currently targeting that enemy for the total weight to be applied (so the more team members targeting that one enemy, the stronger the overall weight).
The relative-distance-weight is used to help prioritize based on distance to the chosen target. It searches for fellow AI team members that are targeting the same target within the specified relative-search-radius, and scales the relative-distance-weight based on who is closest to that target. This helps to solve the problem of AI on the same team picking targets that they are furthest from, yet other team members are closer.
The vulnerability weights are used to help boost targeting based on certain conditions. So in this case, there are weighting bonuses for when the target is running out in the open, aim-moving out in the open (this only effects the player), and standing out in the open.
The preferred-target-weight is a way for designers to specify specific targets. The way it works is we have the option at any time to specify something as a preferred target to any specific AI. This means that when that AI runs a target evaluation, anything marked as a preferred target for that AI will get this extra weight applied as a part of the evaluation.
So as you can see we have a lot of options to influence how the AI will select their targets. We can set up as many sets of targeting parameters as we like and assign them to various archetypes, or even on specific AI placed in levels. Also a lot of these weights can be set to negative values, thus giving us more flexibility based on the context of the scenario.
And all of that makes up an AI archetype! There are other parameters that aren't covered in this article (such as testing- or prototype-specific ones), but these are the bulk of what's used. By creating combinations of these parameters we can create AI types that behave differently as well as rapidly prototype new ideas. Most of the power is also in the designer's hands and only requires programmer intervention when we need a new skill or specific behavior (which is also quick to prototype thanks to the skill list). So let's move on to how we set up the remaining parts.
When it comes to setting the look of the AI (both visually and with animation), again we have a great amount of flexibility. For the visuals, we have a parts based "looks" system that allows us to compartmentalize the characters however we wish.
For example with the light and medium class enemies we had a basic body (without hands or head), various heads (with hands included to keep the skin-tone matched), and a collection of gear we could attach however we wished. In a script file we would define a "look" as a specific collection of these items and then assign them to an archetype.
Also you can create a collection, and then attach that entire collection to another collection. We used this feature extensively to create head varieties that we could swap out randomly. So as an example, we had several classes of guys that had specific gear attached to the head (like a specific type of hat or mask). We would create several collections just for the heads with their gear, and then at run time when the AI spawns it would pick one of these collections at random to use for the head.