The enemy AI needs to take into the account multiple players and players’ summons, which wasn’t a simple thing. Fortunately, I planned multiplayer for a long time, so while coding the AI, I implemented special aggro mechanics, which work amazingly well! This is a very interesting topic for me.
Each enemy has 4 possible states: “chase”, “wander”, “runaway”, “dead”. The default one is “wander” – it means that the monster will keep walking around the spawnpoint, pausing here and there. State “chase” means that it goes after a player. State “runaway” is what “chase” changes into, if the monster is below set amount of health – it is configured on per-monster basis and can have an added effect of randomness (ex. if health < 20%, then it has a 10% chance to trigger “runaway” every 0.5 sec). And I guess “dead” is pretty clear.
While enemy is chasing, it has 2 lists of attackers, and 3 values for every attacker. List 1 is “Targets seen” which has list of all targets the enemy can see right at this moment. List 2 is “Targets remembered“, which consists of every target the monster has seen or was damaged by, and hasn’t forgotten yet. Now to the values…
1. Aggro points – every player gets 100 aggro points for every 1 second they stay in enemy’s field of view – maximum is 500 and 50 is lost every second outside of field of view. So after being seen for 2 seconds, you need to stay hidden for 4 to be forgotten. 10 points is added to an attacker’s balance for each attack regardless of damage.
2. Mad points – they are added to attacker’s balance when they attack – the formula for Mad Points added = 1000 * damageTotal / (attacker’s level)^2 …the point of that is that if 2 players are attacking a strong enemy, while one player is level 10 and the other is level 30, the enemy is more likely to chase the weaker guy if they deal similar damage. It might be worth the experience, but it will make hunting dangerous stuff with a strong friend much riskier. The maximum balance is 1000, and 20 points are lost per second. And Mad Points are maximized almost instantly for player/summon if he blocks the way to the target.
3. Focus points – it’s a sum of Aggro Points and Mad Points. It’s used to decide who the creature should focus, and that’s it. When this value reaches 0, the target is “forgotten” – removed from all lists
Here’s a part of configuration of enemy-specific information regarding the AI:
Each enemy has a few different trigger elements, let me use Cyclops as an example:
The image above shows the enemy and all its trigger boxes.
Trigger 1 – the large triangular shape is field of view. When hostile entity (player or his summon) enters it, the monster checks if there are no environmental objects on the way by raycasting towards it, and if the way is clear the monster “notices” his target, and keeps adding Aggro Points to target’s account.
Trigger 2 – the little cube shape is the box where Melee Attack is possible.
Trigger 3 – long rectangular shape is where Ranged Fire Bolt attack is possible.
Trigger 4 = large octagonal shape is the triggerbox for Stomp area of effect attack.
Let’s use Trigger 2 as an example. When player is in this trigger box, the creature will keep running a timer for ability associated with the trigger (in this case, it’s Cyclops Melee Attack), and each time the timer goes off (it’s 0.1 sec in the example), it will randomize a number in 0-100 range, and if it’s below or equal set probability (in this case it’s 100, so always will attack), it will attempt an attack. An attempt means exactly the same thing as player clicking ability/attack button – it will attack as long as the caster is not stunned, isn’t already in the middle of another action, isn’t asleep, silenced, and so on. Here’s how the component managing the abilities looks:
All of the above are tables, first element of each corresponds to other first elements, and so on.
Ranges list is the list of objects with triggers – the shapes seen on the image before.
Attack chance is % chance of using this ability every time the attempt cooldown goes off.
Attack Cooldown Timer is actual timer used in game, nothing to set here.
Attack Cooldown List is the time it rolls a dice regarding attack chance – it is varied to avoid 2 consecutive very strong attacks most of the time, in other cases it can be set it 0.1 sec
Attacks in Range contains booleans which indicate whether a target is present in related trigger or not
So a real examaple – target is present in ShootTargetHelp trigger (number 3 on the Cyclops image). It randomizes 0-100, and the result is 55 – which is above the 30% threshold (attack chance, element 1), so nothing happens. The timer is set to 0.4 sec, as configured in Attack Cooldown List, after that time passes, another number gets randomized and this time it’s 15, so below the limit. Cyclops sends a raycast in targets direction (optional requirement), and nothing is in the way, so the attack is attempted. Cyclops begins the animation and shoots a firebolt.
The attacks higher in the list have the priority (ex. element 0 > element 2).