AI:BrainTutorialLua
- Last edited 12 years ago by I need to Update My Profile
Creating a new brain in lua will allow you to override the default AI, it will not allow you to extend the default AI however. You can change the script and reload it without having to recompile and restart the server however. LUA AI needs to cover every aspect of combat, including putting the NPC into combat. In this tutorial I will show you how to create a lua AI that will duplicate the default AI code in the server core.
Set Up the Spawn Script
First thing you need to do is tell the spawn to use the AI defined in the lua script, this is done by calling SetLuaBrain(), you can also tell the script how often the AI code needs to run with SetBrainTick()
function spawn(NPC) SetLuaBrain(NPC) SetBrainTick(NPC, 200) end
You will also want to make sure the brain is set when they respawn
function respawn(NPC) spawn(NPC) end
Finally you need to add the "Think" function, this will be called every time the AI code needs to be run. The server will pass 2 parameters to this function, the first is the NPC this spawn script is attached to, the second is the NPC's current target.
function Think(NPC, Target) end
We are now ready to program the AI.
Programing The New Brain
The first thing we need to do is check to see if the NPC can do anything, if they are mezzed or stunned then they can't do anything and we can skip the rest of the code. We can check if this is the case by calling IsMezzedOrStunned(), this will return true if the NPC is mezzed or stunned
-- Make sure the NPC is not mezzed or stunned if not IsMezzedOrStunned(NPC) then end
If the NPC is not mezzed or stunned the code inside the if will execute, if it is all the code will be skipped. We will now check the NPC's hate list and get the spawn that this NPC hates the most
-- Get the most hated spawn local mostHated = GetMostHated(NPC)
We will also need to get the distance the spawn is to its run back location, the run back location is set to the position the NPC is at when they are flagged for combat
-- Get runback distance local runback_distance = GetRunbackDistance(NPC)
Now we should check to see if we got a hate target, if we don't have a hate target we will still do some stuff so we will use an else as well
-- Check to see if we got a mostHated
if mostHated ~= nil then
else
-- nothing in hate list
end
We will work with the mostHated spawn first, so lets check to see if we have a current target, if we do we will compare it to the mostHated spawn with CompareSpawns(), if the spawns are not the same, or we don't have a target, we want to set the NPC's target to the mostHated spawn
-- Check to see if the target and the most hated are the same if Target == nil or not CompareNPCs(mostHated, Target) then -- Not the same so lets make them the same SetTarget(NPC, mostHated) FaceTarget(NPC, mostHated) end
We should now check to see if the NPC is flagged in combat with IsInCombat(), if it isn't we should put it into combat with SetInCombat(), also note that setting the NPC into combat will suspend scripted movement until it is no longer in combat.
-- We have a hate target so lets check if we are already in combat if not IsInCombat(NPC) then -- Not in combat so lets put the NPC in combat SetInCombat(NPC, true) end
Now lets check the runback_distance to see if it exceeds the max distance. First we should define the max distance at the top of the script, we will set it to 80 to match the server core.
local MAX_CHASE_DISTANCE = 80
Back to where we were we can now check the distance, if the distance is greater then the value we define we should clear the hate list with ClearHate() and clear the encounter list with ClearEncounter(), this will bring the NPC out of combat.
-- Check distance to run back location if runback_distance > MAX_CHASE_DISTANCE then -- Clear the hate list ClearHate(NPC) -- Clear the encounter list ClearEncounter(NPC) else
If we haven't exceeded the max distance we can now do combat stuff, first we should check to see if the NPC is casting a spell with IsCasting(), if not casting we should check to see if it has recovered from casting with HasRecovered() and finally we should try to cast a spell with ProcessSpell() if we failed to cast a spell we should try a melee attack with ProcessMelee(), this will also cause the spawn to move closer to its target if it is outside melee range. We will check all but melee in a single if. We also need to get the distance between two spawns with GetDistance().
else -- Get the distance between the spawns for combat calcs local distance = GetDistance(NPC, Target, 1) -- Check if not currently casting and the NPC has not recovered from casting or can cast a spell if not IsCasting(NPC) and (not HasRecovered(NPC) or not ProcessSpell(NPC, mostHated, distance)) then FaceTarget(NPC, mostHated) -- Attempt a melee attack, will also make the NPC move close to its target if it is out of range ProcessMelee(NPC, mostHated, distance) end end
And that is it for the "if mostHated ~= nil then", we will now go into the else portion of the code for when there is no one in the hate list. First we should check to see if the NPC is still in combat, if it is we should bring it out of combat. The NPC should be set to full health if it is not the pet of a player. To set the HP to full we can use SetHP() and get the max HP for the NPC with GetMaxHP(). To check if it is a pet we would use IsPet(), if it is a pet we can get the owner with GetOwner() and check if the owner is a player with IsPlayer().
else -- nothing in hate list -- check to see if the NPC is still flagged in combat if IsInCombat(NPC) then -- Still in combat, lets take the NPC out of combat SetInCombat(NPC, false) -- Check if not a pet, or if a pet and the owner is not a player if not IsPet(NPC) or not IsPlayer(GetOwner(NPC)) then -- Set HP to max SetHP(NPC, GetMaxHP(NPC)) end end
Now lets check to see if the NPC has a runback location by checking if runback_distance is greater then 0, if it is we should run back with Runback()
-- Check to see if we have a run back location if runback_distance > 0 then -- We have a run back location so lets go to it Runback(NPC) end
Finally lets check the encounter size with GetEncounterSize() to ensure it is empty, after all if this NPC is not in combat then there should not be any one in its encounter list.
-- Nothing in the hate list so we are not in combat so no one should be in our encounter list if GetEncounterSize(NPC) > 0 then ClearEncounter(NPC) end
And that is it, this script will replicate the default AI the server core uses for the vast majority of spawns.
Complete Script
local MAX_CHASE_DISTANCE = 80 function Think(NPC, Target) -- Make sure the NPC is not mezzed or stunned if not IsMezzedOrStunned(NPC) then -- Get the most hated spawn local mostHated = GetMostHated(NPC) -- Get runback distance local runback_distance = GetRunbackDistance(NPC) -- Check to see if we got a mostHated if mostHated ~= nil then -- Check to see if the target and the most hated are the same if Target == nil or not CompareNPCs(mostHated, Target) then -- Not the same so lets make them the same SetTarget(NPC, mostHated) FaceTarget(NPC, mostHated) end -- We have a hate target so lets check if we are already in combat if not IsInCombat(NPC) then -- Not in combat so lets put the NPC in combat SetInCombat(NPC, true) end -- Check distance to run back location if runback_distance > MAX_CHASE_DISTANCE then -- Clear the hate list ClearHate(NPC) -- Clear the encounter list ClearEncounter(NPC) else -- Get the distance between the spawns for combat calcs local distance = GetDistance(NPC, Target, 1) -- Check if not currently casting and the NPC has not recovered from casting or can cast a spell if not IsCasting(NPC) and (not HasRecovered(NPC) or not ProcessSpell(NPC, mostHated, distance)) then FaceTarget(NPC, mostHated) -- Attempt a melee attack, will also make the NPC move close to its target if it is out of range ProcessMelee(NPC, mostHated, distance) end end else -- nothing in hate list -- check to see if the NPC is still flagged in combat if IsInCombat(NPC) then -- Still in combat, lets take the NPC out of combat SetInCombat(NPC, false) -- Check if not a pet, or if a pet and the owner is not a player if not IsPet(NPC) or not IsPlayer(GetOwner(NPC)) then -- Set HP to max SetHP(NPC, GetMaxHP(NPC)) end end -- Check to see if we have a run back location if runback_distance > 0 then -- We have a run back location so lets go to it Runback(NPC) end -- Nothing in the hate list so we are not in combat so no one should be in our encounter list if GetEncounterSize(NPC) > 0 then ClearEncounter(NPC) end end end end function spawn(NPC) SetLuaBrain(NPC) SetBrainTick(NPC, 200) end function respawn(NPC) spawn(NPC) end