**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) ```