Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Advanced AI targetting system ("threat")


Joost
 Share

Recommended Posts

**The current way things work**
The NPC hits the last thing that attacked it. Whether this was the guy with the huge sword or the guy giving him a backrub is irrelevant, you touch me last, I kill you now.  This also has the side-effect of the NPC spasming out while trying to attack every single one of the 4 people hitting him at the same time.

**What this does**
This gives all NPC's a list of a number between ~~3 and 5  targets~~ 15 targets and how much damage they did to that NPC. The NPC will always attack the person who did the most damage to it. This means that you can have a setup with one warrior class tank designed for keeping the NPC's attention while the mages and rogues do damage to the NPC. The WoW definition of threat (what this simulates) is

"_Threat is a measure of an NPC's aggression (often called "aggro") towards a player. Each NPC has a threat table, and the character at the top of the list is usually the target of its aggression. In-game, this is known as having aggro from that particular NPC. The NPC will attack that character if possible, unless another character manages to change the NPC's target. The tanking role usually has to pay particular attention to threat and how to hold it_."

**What is tested**
Every possible scenario involving 2 players.

**What's still missing**
It doesn't take into account healing as a mechanic of getting threat.

**The code**
All of this stuff is server-side of course, since server handles everything AI-related.

First of all, some subs
```
Sub CheckTarget(ByVal MapNum As Long, ByVal MapNpcNum As Long)
Dim i As Byte, CurrentThreat As Long, y As Byte

For i = 0 To 14
        'MsgBox (x)
    If MapNpc(MapNum).Npc(MapNpcNum).threat(i) > CurrentThreat Then
        CurrentThreat = MapNpc(MapNum).Npc(MapNpcNum).threat(i)
        y = i
    End If
Next i
If CurrentThreat = 0 Then
    MapNpc(MapNum).Npc(MapNpcNum).Target = 0
    MapNpc(MapNum).Npc(MapNpcNum).targetType = 0
Else
    MapNpc(MapNum).Npc(MapNpcNum).Target = MapNpc(MapNum).Npc(MapNpcNum).Posstarget(y)
    MapNpc(MapNum).Npc(MapNpcNum).targetType = 1
End If

End Sub

Sub AdjustThreat(ByVal MapNum As Long, ByVal MapNpcNum As Long, ByVal Index As Long, ByVal Damage As Long)
Dim i As Byte, Updated As Boolean

'This is if you have like a warrior class that you want to have tanking
'If GetPlayerClass(Index) = WARRIOR Then
'    Damage = Damage * 1.3
'    Damage = Damage + 50
'End if

For i = 0 To 14
    If MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = Index Then
        'Check if player is already in threat list
        MapNpc(MapNum).Npc(MapNpcNum).threat(i) = MapNpc(MapNum).Npc(MapNpcNum).threat(i) + Damage
        Updated = True
        Exit For
    End If
Next i

If Updated = False Then
    For i = 0 To 14
        If MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = 0 Then
            MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = Index
            MapNpc(MapNum).Npc(MapNpcNum).threat(i) = Damage
            Updated = True
            Exit For
        End If
    Next i
End If

End Sub

Sub RemoveThreat(Index As Long, MapNum As Long, MapNpcNum As Long)
Dim i As Byte

For i = 0 To 14
    If MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = Index Then
        MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = 0
        MapNpc(MapNum).Npc(MapNpcNum).threat(i) = 0
        Exit For
    End If
Next i

Call CheckTarget(MapNum, MapNpcNum)
End Sub

Sub ResetThreat(ByVal MapNum As Long, MapNpcNum As Long)

Dim i As Byte
For i = 0 To 14
    MapNpc(MapNum).Npc(MapNpcNum).Posstarget(i) = 0
    MapNpc(MapNum).Npc(MapNpcNum).threat(i) = 0
Next i
End Sub

```
Let's go trough them one by one. Sub CheckTarget is just something that checks what target on the NPC's list is highest on the list and should be attacked next. Sub AdjustThreat gives a player his threat after doing damage. The threat given is equal to the damage done. RemoveThreat and ResetThreat are both ways of clearing the memory, ResetThreat is for players(logoff), RemoveThreat is for the entire list (npc dies).

In the Private Type MapNpcRec, we need to define the new properties, so add the below code in there. This just defines the variables that keep track of all the aggro list for all NPC's
```
    Posstarget(0 To 15) As Long
    threat(0 To 15) As Long
```
Now, onto the part that handles NPC attacking,
```
                ' /////////////////////////////////////////////
                ' // This is used for npcs to attack targets //
                ' /////////////////////////////////////////////
```
We need to change the code that sets the target there to :
```
                If Map(MapNum).Npc(x) > 0 And MapNpc(MapNum).Npc(x).Num > 0 Then
                    Call CheckTarget(MapNum, x)
                    Target = MapNpc(MapNum).Npc(x).Target
                    targetType = MapNpc(MapNum).Npc(x).targetType

                    ' Check if the npc can attack the targeted player player
                    If Target > 0 Then

                        If targetType = 1 Then ' player

                            ' Is the target playing and on the same map?
                            If IsPlaying(Target) And GetPlayerMap(Target) = MapNum Then
                                TryNpcAttackPlayer x, Target
                            Else
                                ' Player left map or game, set target to 0
                                Call RemoveThreat(Target, MapNum, x)
                                'MapNpc(MapNum).Npc(x).targetType = 0 ' clear
                            End If
                        Else
                            ' lol no npc combat :(
                        End If
                    End If
                End If
```
In Public Sub SpawnNPC we need to add
```
        Call ResetThreat(MapNum, MapNpcNum)
```
And after that we only need to call the add threat sub when a player damages an NPC. Below

```
      ' send animation
        If n > 0 Then
            If Not overTime Then
                If spellnum = 0 Then Call SendAnimation(MapNum, Item(GetPlayerEquipment(attacker, Weapon)).Animation, 0, 0, TARGET_TYPE_NPC, MapNpcNum)
            End If
        End If
```

You need to change whatever there's into
```
      'Adjust the threat
        Call AdjustThreat(MapNum, MapNpcNum, attacker, Damage)

        'If required, adjust target
        Call CheckTarget(MapNum, MapNpcNum)
        ' Set the NPC target to the player
```
And you're done. Pretty simple I guess.

**EDIT**

I knew I missed something. in Sub NpcAttackPlayer, change
```
        ' Set NPC target to 0
        MapNpc(MapNum).Npc(MapNpcNum).Target = 0
        MapNpc(MapNum).Npc(MapNpcNum).targetType = 0
```into
```
        ' Set NPC target to 0
            Call RemoveThreat(victim, MapNum, MapNpcNum)
            Call CheckTarget(Mapnum, MapNpcNum)
```

The attachment has one small error server-side, just try running it. Obvious fix is changing it into
```
            Call CheckTarget(MapNum, MapNpcNum)
```
Link to comment
Share on other sites

@Anosora:

> I like the idea but you will need to test everything before posting.

Really? Do u think everything is fully tested before it is posted here? Absolutely not…

Good job joost I like this keep us posted on anything that comes up :)
Link to comment
Share on other sites

Updated the code a bit to make sure I never find out what happens if there are too many people on the threat list by increasing the threat list to 15\. The only problem that can arise atm is the list not being reset properly at some point (works when player leaves map, disconnects, or warps but there might be other reasons) and that would be easily fixable. I'd say this is ready to be added to any game, especially since it is such a major major improvement over the old AI.
Link to comment
Share on other sites

  • 2 weeks later...
@Joost:

> I am working on something really cool now, will maybe add a taunting tutorial after that. It is pretty easy to rip the code too, just find all instances of Taunt and copy them.

Well, thats the problem, I can't find the word 'Taunt' in both client and server.
Link to comment
Share on other sites

I like the idea but, im trying to understand what it does. You say you can set up a warrior tank class to be the meatshield and you set a 15 players list that can be potential targets for monsters. Still, i dont understand how it is supposed to work ? Monsters will still go for the player who did the most damage or there is some kind of ''threat'' related to a particuliar class ?

Id like to add this system to my game but i cant figure out how it works.
Thanks
Link to comment
Share on other sites

You have it right a monster will attack whoever deals the most damage to it. He also included a taunt spell in his source for warrior classes..

Joost I was wondering what happens when 4 players block a monster and another person stands from afar and attacks while the others just stand there. Will it not attack anyone cause it can't reach the attacking player or will it give up since it can't reach him and attack one of the 4 players blocking it?
Link to comment
Share on other sites

@Justn:

> You have it right a monster will attack whoever deals the most damage to it. He also included a taunt spell in his source for warrior classes..
>
> Joost I was wondering what happens when 4 players block a monster and another person stands from afar and attacks while the others just stand there. Will it not attack anyone cause it can't reach the attacking player or will it give up since it can't reach him and attack one of the 4 players blocking it?

Not joost's problem really, that would be a problem with EO's AI regardless of Joost's system.
Not sure if Robin ever did anything to prevent this.
Link to comment
Share on other sites

I know its not Joost's problem.. but he is trying to fix the ai targeting.. I think that classifies as ai targeting.. just trying to help improve things. I think the ai needs to be completely changed anyways gonna work on it. No sense in adding this to an ai with holes already in it. Someone also told me that this isn't good for bigger servers due to the messy code? Just thought I would share.. ill let u know Joost if I come up with any improvements to the rest of the ai
Link to comment
Share on other sites

It adds 2x15 longs to every NPC that's on a map, to save the damage and the index of the player (15 poss. players) Besides that, I'm not sure what's messy about it. (well, it is messy but nothing that should slow anything down) From my point of view this is such a major improvement of the old targetting system (where you could "bounce" a mob between 2 players constantly) that it is worth that. It allows proper party systems with tanks and dps and healers and makes any game feel more online and also creates unique roles for classes.

The thing you mention with 4 ppl surrounding and 1 person damaging is correct, it will constantly try to run to the 5th player and fail. But yeah, this is a problem w/ the original code and w/ mine.

For the taunting, I just added an extra spell variable, taunt as boolean in the spellrec, added checkbox in spelleditor and in the sub castspell (or handlespell, i dunno, whatever) I did a check if spell = taunt then find highest aggro and set taunters aggro to 110% of that. Not really difficult to do and a good training for new programmers so not too worried about the code being lost.
Link to comment
Share on other sites

@dimz:

> I like the idea but, im trying to understand what it does. You say you can set up a warrior tank class to be the meatshield and you set a 15 players list that can be potential targets for monsters. Still, i dont understand how it is supposed to work ? Monsters will still go for the player who did the most damage or there is some kind of ''threat'' related to a particuliar class ?
>
> Id like to add this system to my game but i cant figure out how it works.
> Thanks

I lost my taunt code but re-doing it is easy, see my above post. For an easy workaround, you can just check if GetPlayerClass(index) = a warrior and then multiple his threat by 300%. There is some commented out code in the subs that will show you what I mean.
Link to comment
Share on other sites

Personally I don't think the 4 players surrounding a boss and a 5th player attacking it is a big deal, hell, I think Runescape has that problem.
The most annoying part about AI in my opinion is the constant move-stop-move-stop when pathfinding or just idly moving around the map.
Of course, this really has hardly any relevance as this thread is about AI targeting, not movement, but yeah. just saying.
Uhh what can I say that's on topic so I don't get warned.
Well it's a nice code, I'm not entirely sure if it's ever been done and **released** before, but it has been a common request, well, it used to be a few years ago.
It's definitely an improvement over the previous AI and every game should probably include this, although alter it to suit the game if necessary.

**PS**
It's kinda funny that Joost hasn't even got 100 posts yet and is more of a contribution to the community than like at least 3/4 of all registered users, if not more.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...