--
-- $Id: test_threat_ai.lua 2007-02-02 13:15:09 stormcrowsx, Biglad & Sanaell
-- Implementation of threat system as well as spell casting from DB. This initially started as way for
-- me to learn lua and so its a work in progress, but has quickly been evolving to allow the AI to be smarter
-- and more aware.
--
-- Recent Updates: The code now gets the class of the attacking unit and uses it to place a modifier
-- on the damage they have done. I.E. Warriors will get a 20% increase in threat
-- that they cause.
--
-- Rev .103 2/03/07
-- -NPC's will no longer let you pound them from distance
-- -Agro distance was doubled
-- -New function added SetTables(LuaAI, unit) which will add any new unit to the
-- running threat list and further prevents the script from attempting to access
-- data that it doesn't own
--
-- Rev .104
-- -Deleted bugs crashed server
--
-- Rev .105 2/12/07
-- -Added new function TEST_Threat_AI.ClearThreat(LuaAI, unit) which will
-- -clear the threat of the unit that was passed to it, must call with the unit.
--
-- Rev .106 2/12/07
-- -Added new function TEST_Threat_AI.CastSpellClass(LuaAI, classID, spellID)
-- -this allows us to cast class specific spells such as a mana burn that targets
-- -priests
-- -This function also has a boolean return value so if a spell is not casted because it can't
-- -find that class it will let you know
--
-- Rev .107 2/13/07
-- -NPC's will no longer attack each other if they are not hostile to each other, a little
-- -more work will allow them to buff those friendly to them.
-- -Server will no longer crash if the tank is killed or kicked, the threat is now properly
-- -reset on death.
--
-- Rev .108 2/13/07
-- -Clearing threat has been updated so that it will truly clear the threat
-- -previously this was really hacked up and do a weak reset.
-- -Will no longer add NPC's to the threat list unless that npc has done damage to it,
-- -Makes the NPC much less jumpy
--
-- Rev .109 2/13/07
-- -Biglad's code for loading the spells out of the Database was added.
-- -Spells are loaded into the data table as
-- -data.spellID1, data.spellID2 .. etc
-- -Spawn point is loaded into the data table, plan to use this later to make the AI run
-- -back to its spawn point after its ran too far away.
-- -New functions added
-- - TEST_Threat_AI.GetDBInfo(LuaAI) --Done by BigLad, gets the spells out of the database
-- - as well as some additional info such as the creatures spawn point which will be used
-- - used in later revisions to make the creature return to its spawn point
-- -
-- - TEST_Threat_AI.PickRandomSpell(LuaAI) -- This function will
-- - randomly pick one of the spells from the database of the creature and return the spell ID for it.
-- - will return a 0 if the creature does not have any spells
--
-- Rev .110 2/14/07
-- -Using code from biglad implemented the new function DoCastRandomChance that allows randomly casting spells
-- - gave it its own function to help keep the code cleaner
-- -Added support for update so that the NPC will now run update checking code every 3 seconds to
-- - make sure its returned to its spawn point and hasn't chased a PC an absurd distance
-- -Creatures will now return to their spawn point when a PC is dead or run out of distance
-- - this code may need some additional updating to keep the NPC from running back to its
-- - spawn when it still needs to attack another player on the threat list.
--
-- Rev .111 2/14/07
-- -NPC's will now reset their mana and HP to max when resetting.
-- -Modified the Update code to turn it off when the NPC is not doing anything. This will help
-- - this code from being a resource hog when its not needed
--
-- Rev .112 2/14/07
-- -The first of probably many revisions having to do with NPC's targetting the next highest
-- --on their threat list when the primary has died
-- -Updated the Database coding so that it makes sense now :P thanks andrejkw
-- -Re-examined the tables to make them a little more effiecent one was cut since it was more or less
-- --useless. Also cleaned up the functions that deal with the the tables, setting and clearing threat
-- -This file is getting kinda long :\
--
--
-- Rev .113 02/03/07
-- - Spell type chcking added so mob will cast spells on the right target
-- - added dobuff/do debuff ONCE in battle
-- - added check to make sure script doesnt run on players pets
--
-- Rev .114 03/13/07
-- - Added spell cast time store lookup for spell cating times
-- - added new function for detecting pets & totems
-- - added random change option for heal & damage spells
--
-- Rev .115 03/19/07
-- - Added global cool down for all spell cast (5 secs between each cast)
-- - added player & Mob level checking for aggro distance
-- - added aggro check for high lvl players, if player 20+ levels high than mob, aggro range very small
-- - added elite mob check and high cast rate for eilites
-- - clean up code a bit, still lots to clean up once script complete
--
-- Rev .115a 03/19/07
-- - Last of current bugs fixed WE THINK!
-- - Couple of functions optimized and code tweaks by Sanaell
--
-- Rev .116 03/21/07
-- - Code update & tweakes
-- - Some functions updates/redone
-- - more null checking added to fix empty data vaules
--
-- Known Issues: See Threat_TODO.txt
--
--
-- test_threat_ai.lua $release v.116
--
-- Important Notes:If having problem getting the code to load up the database you can do the following:
-- The following code must be inserted into the MasterScript file in order to properly
-- interface with the database
-- sDatabase = DatabaseMysql()
-- sDatabase:Initialize("localhost;3306;username;password;mangos")
-- where username and password are insert what you use to connect to your sql database
-- if your database uses a name other than mangos this will need to be changes as well
--
-- Scripting developed by:
-- Stormcrowsx
-- Biglad
-- Sanaell
--
-- Thanks To:
-- MangoScript Dev Team - Providing tools neccessary to quickly compile and test scripts
-- Hakon, SinnerG, andrejkw & Derex for advise & help :)
TEST_Threat_AI = Mango.AIScripts.Create('test_threat_ai');
-- Modifiers for the threat, this helps the warriors, paladins, and druids to tank
-- due to the lack of working taunts
-- Not working until I find a way to get the classID from the units
-- Edit these numbers to change the way agro works
-- 1.2 for instance means 20% is added to threat
-- 0.8 would mean 20% is knocked off the damage
-- Warrior , Paladin , Hunter , Rogue , Priest , Unknow , Shaman , Mage , Warlock , Unknow , Druid
TEST_Threat_AI.Modifier = { 1.2,1.1,0.9,0.8,0.9,1,1.0,0.9,0.9,1,1.0};
-- Function prototypes (to keep it clean)
local TEST_Threat_AI_Init;
local TEST_Threat_AI_MoveInLineOfSight;
local TEST_Threat_AI_AttackStart;
local TEST_Threat_AI_DamageInflict;
local TEST_Threat_AI_OnDeath;
local TEST_Threat_AI_SetModifier;
local TEST_Threat_AI_LevelAggroCheck;
local TEST_Threat_AI_AggroDisCheck;
-- local TEST_Threat_AI_AttackStop;
function TEST_Threat_AI_IsVisible(LuaAI,unit)
return LuaAI:IsVisible_agr(unit,1500)
end
function TEST_Threat_AI_MobModifier(LuaAI)
local issemiuber = LuaAI:GetCreature():isElite();
TEST_Threat_AI.ChanceToCastOnDamage , TEST_Threat_AI.ChanceToHealOnDamage = 30 , 60;
if ( issemiuber ) then
TEST_Threat_AI.ChanceToCastOnDamage , TEST_Threat_AI.ChanceToHealOnDamage = 40 , 75;
end
end
function TEST_Threat_AI.DoCastRandomChance(LuaAI, target, chance, spellID, casttime1)
local letsDoSpell = rand_range(1,100)
data.casting = 0
if ( letsDoSpell >= ( 100 - chance ) ) and ( spellID > 0 ) then
--Need spell cast time support cast times for spells are in AI system alrdy casttime1 = cast time in ms
LuaAI:DoCast(target, spellID);
end
end
function TEST_Threat_AI.CheckPet(LuaAI)
if ( LuaAI:GetCreature():isTotem() ) or ( LuaAI:GetCreature():isPet() ) then
return true
end
return false
end
function TEST_Threat_AI.CheckSpell(LuaAI, mobspellid, spellno)
if mobspellid >= 1 then
local data = LuaAI:Data()
spell , spelltime , casttime = 0
if ( sSpellStore:LookupEntry(mobspellid) ~= nil ) then
spell = sSpellStore:LookupEntry(mobspellid)
if sCastTimesStore:LookupEntry(spell.CastingTimeIndex) ~= nil then
-- need to fix this, something changed in core and spelltime is now null
spelltime = 2500
casttime = 2500
--spelltime.CastTime = 2500
--spelltime = sCastTimesStore:LookupEntry(spell.CastingTimeIndex)
--if spelltime.CastTime ~= nil then
--casttime = spelltime.CastTime
--end
end
end
--check to so who the spell is for
for i = 0, 3, 1 do
if ( spell:EffectImplicitTargetA(i) == TARGET_SINGLE_FRIEND ) then
data.targetfriendly = true
data.targetfriendlyspell = mobspellid
end
if ( spell:EffectImplicitTargetA(i) == TARGET_CHAIN_DAMAGE ) or ( spell:EffectImplicitTargetA(i) == TARGET_ALL_ENEMY_IN_AREA ) then
data.targetplayer , data.targetplayerspell = true , mobspellid
end
end
--if spell for mob/friendly check type of spell heal or buff
if ( data.targetfriendly ) then
--check for healing spell
for i = 0, 3, 1 do
if ( spell:Effect(i) == SPELL_EFFECT_HEAL ) or ( spell:Effect(i) == SPELL_EFFECT_SCRIPT_EFFECT ) or ( spell:Effect(i) == SPELL_EFFECT_HEAL_MAX_HEALTH ) then
data.Canheal = true
data.castimewaitheal = casttime
data.healspell = data.targetfriendlyspell
end
--check for buff spell
if ( spell:Effect(i) == SPELL_EFFECT_APPLY_AURA ) then
data.buffspell = true
data.dobuffspell = data.targetfriendlyspell
end
--check spell for self cast or friendly
data.targetfriendlymob = true
data.targetfriendlyspell = mobspellid
if ( spell:EffectImplicitTargetA(i) == TARGET_SELF ) then
data.targetself = true
data.selfcastonlyspell = data.targetfriendlyspell
end
end
end
--if spell for player/group check type of spell damage or debuff
if ( data.targetplayer ) then
--data.cancastdmgspell = "do"..spellno;
--check for healing spell
for i = 0, 3, 1 do
if ( spell:Effect(i) == SPELL_EFFECT_SCHOOL_DAMAGE ) or ( spell:Effect(i) == SPELL_EFFECT_INSTAKILL ) or ( spell:Effect(i) == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE ) or ( spell:Effect(i) == SPELL_EFFECT_HEALTH_LEECH ) then
data.dmgspell = true
data.castimewaitdmg = casttime
data.dodmgspell = data.targetplayerspell
end
--check for debuff spell
if ( spell:Effect(i) == SPELL_EFFECT_APPLY_AURA ) then
data.debuffspell = true
data.castimewaitdebuff = casttime
data.dodebuffspell = data.targetplayerspell
end
--check spell for single cast or group
data.targetgroup = true
data.targetgroupspell = data.targetplayerspell
if ( spell:EffectImplicitTargetA(i) == TARGET_CHAIN_DAMAGE ) then
data.targetsingleplayer = true
data.targetsingleplayerspell = data.targetplayerspell
end
end
end
end
data.candobuffspell , data.candodebuffspell , data.casting = 0
LuaAI:SetData(data)
end
function TEST_Threat_AI.GetDBInfo(LuaAI)
-- function to pull spell and spawnpoint info
local data = LuaAI:Data();
data.SpawnPosX , data.SpawnPosY , data.SpawnPosZ = 0
for i= 1, 4 do
data["spellID"..i] = 0
end
data.moblevel = 1
if ( not TEST_Threat_AI.CheckPet(LuaAI) ) then
creatureGUID = LuaAI:GetCreature():GetGUIDInst();
-- debug for GUID
-- LuaAI:GetCreature():MonsterSay("My GUID: " ..creatureGUID,0);
queryString = "SELECT `id` , `spawn_position_x` , `spawn_position_y` , `spawn_position_z` FROM `creature` WHERE `guid`=" .. creatureGUID;
nameid = sDatabase:Query(queryString)
if nameid == nil then LuaAI:GetCreature():MonsterSay("SPELL ERROR! please, tell about it to your Administrator!",0); end
if nameid ~= nil then
getcreatureID = nameid:Fetch()
data.SpawnPosX = getcreatureID[1]:GetInt32()
data.SpawnPosY = getcreatureID[2]:GetInt32()
data.SpawnPosZ = getcreatureID[3]:GetInt32()
local creatureID = getcreatureID[0]:GetString()
queryString2 = "SELECT `maxlevel`, `spell1`, `spell2`, `spell3`, `spell4` FROM `creature_template` WHERE `entry`=" .. creatureID;
result1 = sDatabase:Query(queryString2);
if result1 ~= nil then
result = result1:Fetch();
--Store in the data table so we can easily make use of it later
data.moblevel = result[0]:GetInt32()
for i= 1, 4 do
data["spellID"..i] = result[i]:GetInt32()
end
end
for i= 1, 4 do
if data["spellID"..i] > 0 then
if sSpellStore:LookupEntry(data["spellID"..i]) then
TEST_Threat_AI.CheckSpell(LuaAI, data["spellID"..i], i)
end
end
end
end
end
LuaAI:SetData(data)
end
function TEST_Threat_AI_LevelAggroCheck(LuaAI, player)
if ( TEST_Threat_AI.CheckPet(LuaAI) ) then
local data = LuaAI:Data();
if player:getLevel() ~= nil then
local playerslevel = tonumber(player:getLevel())
local mobslevel = tonumber(data.moblevel)
local lvldif = 1
if playerslevel and mobslevel and tonumber(player:getClass()) ~= 0 then
lvldif = tonumber((mobslevel - playerslevel))
TEST_Threat_AI_AggroDisCheck(LuaAI, player)
if lvldif < -19 then
return true
end
end
end
end
end
function TEST_Threat_AI_AggroDisCheck(LuaAI, player)
local data = LuaAI:Data();
local playerslevel = player:getLevel()
local mobslevel = data.moblevel
local lvldif = (mobslevel - playerslevel)
TEST_Threat_AI.AgroDistance = 75
local diftst = 0
for i= -10, 10, 5 do
if lvldif >= i and lvldif < i + 5 then
TEST_Threat_AI.AgroDistance , diftst = TEST_Threat_AI.AgroDistance + 25 , 1
end
if ( diftst ~= 1 ) then
TEST_Threat_AI.AgroDistance = TEST_Threat_AI.AgroDistance + 50
if lvldif > 10 then TEST_Threat_AI.AgroDistance = 400 end
if lvldif < -10 then TEST_Threat_AI.AgroDistance = 75 end
end
end
end
--Picks a random spell from its spell listings
function TEST_Threat_AI.PickRandomSpell(LuaAI)
local data = LuaAI:Data()
local spells = { data.spellID1 , data.spellID2 , data.spellID3 , data.spellID4 };
local randSpells = { }
local highRoll , highRollerID = 0 , 1;
for i=1, 4 do
randSpells[i] = 0;
if (spells[i] > 0) then
randSpells[i] = rand_range(1,100);
end
if ( randSpells[i] > highRoll ) then
highRoll , highRollerID = randSpells[i] , i
end
end
return spells[highRollerID];
end
-- Function will allow targetting specfic classes for spells
function TEST_Threat_AI.CastSpellClass(LuaAI, classID, spellID)
-- List of classID's that are valid to pass to it
-- classID = 1 Warrior
-- classID = 2 Paladin
-- classID = 3 Hunter
-- classID = 4 Rogue
-- classID = 5 Priest
-- classID = 7 Shaman
-- classID = 8 Mage
-- classID = 9 Warlock
-- classID = 11 Druid
-- Unknown = 0 was returning this when it was tracking nearby critters, possibly critter classID or npc classID
local data = LuaAI:Data()
local units = data.UnitList
local counter = ( data.TableCounter - 1 );
local indexNum = 0
local found = false;
for i=1, counter do
if ( not found ) then
if (units[i]:getClass() == classID) then
found = true;
indexNum = i
end
end
end
if ( found ) then
LuaAI:DoCast(units[indexNum], spellID);
return true;
end
return false;
end
function TEST_Threat_AI_EraseThreat(LuaAI)
local data = LuaAI:Data();
-- List of damage done by units --List of units on our threat list
data.threatListDamage = { };
data.UnitList = { };
data.TableCounter = 1
data.candodebuffspell = 0
data.candobuffspell = 0
data.casting = 0
data.StartAttack = false;
data.wasAttacked = false;
data.update = false;
data.TableCounter = 1; --The number of attackers (due to lua indexing starts at 1 not 0)
LuaAI:SetData(data)
LuaAI:SetUpdateInterval(0)
end
function TEST_Threat_AI.CheckThreatList(LuaAI)
local data = LuaAI:Data();
local threatListDamage = data.threatListDamage
local units = data.UnitList
local counter = data.TableCounter
local newUnits = { };
local newThreatListDamage = { };
local newCounter = 1;
if ( counter > 1) then
for i = 1, counter - 1 do
unit = units[i];
if ( unit:isTargetableForAttack() ) then
if ( ( LuaAI:GetCreature():GetDistanceSq(unit) < 1500 ) and ( unit:isInAccessablePlaceFor(LuaAI:GetCreature()) ) ) then
table.insert(newUnits, newCounter, unit);
table.insert(newThreatListDamage, newCounter, threatListDamage[i]);
newCounter = newCounter + 1;
end
end
end
data.TableCounter = newCounter;
LuaAI:SetData(data);
if ( newCounter > 1 ) then
data.threatListDamage = newThreatListDamage;
data.UnitList = newUnits;
return true;
end
end
return false;
end
function TEST_Threat_AI.SetTables(LuaAI, unit)
--Sets up the tables for the threat
local data = LuaAI:Data();
local unitID = tonumber(unit:GetGUIDLow()); --Get the unitID
local units = data.UnitList; --load the unit table
local found = false; --is unit in damage table yet?
local counter = data.TableCounter --size of threatlist
local threatListDamage = data.threatListDamage; --Damage tables
--Search for our unit in the attackers table
for i = 1,counter-1 do
unitToCheck = units[i]
if (tonumber(unitToCheck:GetGUIDLow()) == unitID) then
found = true;
end
end
--If we don't find him init tables to include this unit
--Currently pulling agro adds to your dmg table
if ( not found ) then
table.insert(threatListDamage, counter, 75);
table.insert(units, counter, unit);
counter = counter + 1; --Increase the size of threat list
--saves our data collectd
data.threatListDamage = threatListDamage;
data.UnitList = units;
data.TableCounter = counter;
LuaAI:SetData(data);
end
end
function TEST_Threat_AI.GetHighDmg(LuaAI)
local data = LuaAI:Data()
local threatListDamage = data.threatListDamage; --Load in damage table
local highDmg = 0; --Highest damage in table
local units = data.UnitList; --Load in unit table
local highDmgID --Unit who did the most damage (set to 1 but it makes no difference)
local counter = data.TableCounter - 1; --Load in table counter (-1 to prevent from accessing data not ours)
for j = 1, counter do
me , indexNum = threatListDamage[j] , j
if ( me > highDmg ) then
highDmg , highDmgID = me , units[j];
end
end
return highDmgID;
end
function TEST_Threat_AI.CheckAggro(LuaAI)
local data = LuaAI:Data()
local threatListDamage = data.threatListDamage; --Load in damage table
local highDmg = 0; --Highest damage in table
local units = data.UnitList; --Load in unit table
local highDmgID; --Unit who did the most damage (set to 1 but it makes no difference)
local wasCast = false;
--Loops through the damage table looking for highest damage
--Sets the highest ID
if ( TEST_Threat_AI.CheckThreatList(LuaAI) ) then
highDmgID = TEST_Threat_AI.GetHighDmg(LuaAI);
randomSpell = TEST_Threat_AI.PickRandomSpell(LuaAI)
distanceToHighDmg = LuaAI:GetCreature():GetDistanceSq(highDmgID)
--This is the basis for the attacking, its really rough and can use some updating but works for now :)
if ( not data.wasAttacked ) then
TEST_Threat_AI_AggroDisCheck(LuaAI, unit)
if (distanceToHighDmg < TEST_Threat_AI.AgroDistance) then
LuaAI:DoStartAttack(highDmgID);
data.wasAttacked = true;
end
-- Unit is outside of agro range, pull made by spell or ranged attack
else
LuaAI:DoStartAttack(highDmgID);
data.wasAttacked = true;
end
else
LuaAI:DoStopAttack();
data.candodebuffspell , data.candobuffspell , data.casting = 0
end
LuaAI:SetData(data)
end
-- Takes the classID and returns the modifier for that classes damage
-- This is a way to help the rogues not get killed and the tanks to
-- tank.
function TEST_Threat_AI.SetModifier(ClassID)
if ( ClassID ~= nil ) then
if ( ClassID > 0 and ClassID < 12 ) then
return TEST_Threat_AI.Modifier[ClassID]
end
end
return 1
end
function TEST_Threat_AI_Init(LuaAI)
-- Initialize AI data
data = { };
data.threatListDamage = { }; -- List of damage done by units
data.UnitList = { }; --List of units on our threat list
data.StartAttack = false; --Used to start the attack once
data.TableCounter = 1; --The number of attackers (due to lua indexing starts at 1 not 0)
data.wasAttacked = false;
data.update = false;
LuaAI:SetData(data)
-- Get the data from the DataBase
TEST_Threat_AI.GetDBInfo(LuaAI);
LuaAI:SetUpdateInterval(0)
return true
end
function TEST_Threat_AI_Update(LuaAI)
local data = LuaAI:Data();
local AIHP = LuaAI:GetCreature():GetMaxHealth()
local AIMANA = LuaAI:GetCreature():GetMaxPower(0)
--distanceToSpawn = LuaAI:GetCreature():GetDistanceSq(data.SpawnPosX, data.SpawnPosY, data.SpawnPosZ)
if ( ( not LuaAI:GetCreature():isAttacking() ) ) then
TEST_Threat_AI.CheckAggro(LuaAI);
if ( not TEST_Threat_AI.CheckThreatList(LuaAI) ) then
TEST_Threat_AI_Init(LuaAI);
LuaAI:GetCreature():SetPower(0, AIMANA);
LuaAI:GetCreature():SetHealth(AIHP);
LuaAI:DoStopAttack();
end
end
LuaAI:SetData(data);
end
function TEST_Threat_AI_MoveInLineOfSight(LuaAI,unit)
local data = LuaAI:Data()
--LuaAI:GetCreature():MonsterSay("I see you",0);
--Will fail to do anything if its not a hostile, future plans to instead add the unit to a friendly list
--to allow tracking of hp and what it is attacking. This will allow units to help each other
if ( unit:IsHostileTo(LuaAI:GetCreature()) ) and ( not TEST_Threat_AI.CheckPet(LuaAI) ) and ( not TEST_Threat_AI_LevelAggroCheck(LuaAI, unit) ) then
data.update = true
LuaAI:SetUpdateInterval(500)
TEST_Threat_AI.SetTables(LuaAI, unit);
TEST_Threat_AI.CheckAggro(LuaAI); -- Check if the guard has to talk or not :)
LuaAI:MoveInLineOfSight_agr(unit);
--add buff before batle if mob can buff
--Check for Buff
local data = LuaAI:Data();
if data.bufftimer == nil then
data.bufftimer = os.time()
end
local timenow = os.time()
local checkedbufftime = (timenow - data.bufftimer)
if checkedbufftime > 300 then
data.candobuffspell = 0
end
if data.candobuffspell == 0 then
if data.buffspell == true then
spelltocast = data.dobuffspell
--Check buff target
if data.targetself == true then
LuaAI:DoCast(LuaAI:GetCreature(),spelltocast)
spelltarget = LuaAI:GetCreature()
else
--mob can cast spell on a friendly target
--need core support, need to add check for friendly IDs and select target
LuaAI:DoCast(LuaAI:GetCreature(),spelltocast)
spelltarget = LuaAI:GetCreature()
end
end
data.bufftimer = os.time()
data.candobuffspell = 1
data.casttimer = ( os.time() - 10 )
end
end
LuaAI:SetData(data)
end
-- Called at each attack of creature by any victim
function TEST_Threat_AI_AttackStart(LuaAI,unit)
-- Must set tables in case it was pulled at range
-- No check for friendly here as it is already attacking a friendly if it got here :)
TEST_Threat_AI.SetTables(LuaAI, unit);
local data = LuaAI:Data()
TEST_Threat_AI.CheckAggro(LuaAI);
end
function TEST_Threat_AI_DamageInflict(LuaAI,unit,amount_damage)
TEST_Threat_AI.SetTables(LuaAI, unit);
local data = LuaAI:Data()
local unitID = tonumber(unit:GetGUIDLow()); --Load in unitID
local threatListDamage = data.threatListDamage; --Again loading in data tables
local units = data.UnitList --Loads in the unitlist so we can find the class
local indexNum , modifier = 1 --Index of unit that attacked us --Threat Class modifier
local counter = data.TableCounter; --Size of threat list
if ( not data.casttimer or data.casttimer == 0 ) then data.casttimer = os.time(); end
if data.casttimer < os.time() + 5 then
data.casting = 0 --reset can cast
end
--log("%s",tostring(casttimer))
if ( not data.update ) then LuaAI:SetUpdateInterval(500) end
--Find the index number of the attacker
for i = 1 , counter - 1 do
unitToCheck = units[i];
if (tonumber(unitToCheck:GetGUIDLow()) == unitID) then
indexNum = i;
end
end
class = units[indexNum];
classID = class:getClass();
--Add in the attackers damage and save it
amount_damage = amount_damage * TEST_Threat_AI.SetModifier(classID);
local damage = amount_damage + threatListDamage[indexNum];
threatListDamage[indexNum] = damage;
data.threatListDamage = threatListDamage;
data.wasAttacked = true;
LuaAI:SetData(data);
haveUnitsLeft = TEST_Threat_AI.CheckThreatList(LuaAI);
if ( haveUnitsLeft ) then
TEST_Threat_AI.CheckAggro(LuaAI); --Neccessary so it attacks the new target
highDmgID = TEST_Threat_AI.GetHighDmg(LuaAI);
randomSpell = TEST_Threat_AI.PickRandomSpell(LuaAI);
--set spell target based on current random spellid
if (data.cancasttimer ~= 0) and (data.cancasttimer ~= 1) then
data.cancasttimer = 0
end
--Check for heal spell
if ( data.Canheal ) and ( data.casting == 0 ) and ( data.cancasttimer == 0 ) then
local HP = LuaAI:GetCreature():GetHealth();
local MaxHP = LuaAI:GetCreature():GetMaxHealth();
if ( 100 / MaxHP * HP <= 30 ) then
spelltocast = data.healspell
--Check heal spell target
casttime1 = data.castimewaitheal
TEST_Threat_AI_MobModifier(LuaAI);
spelltarget = LuaAI:GetCreature();
TEST_Threat_AI.DoCastRandomChance(LuaAI, spelltarget, TEST_Threat_AI.ChanceToHealOnDamage, spelltocast, casttime1)
data.casting , data.cancasttimer = 1
data.casttimer = os.time();
end
end
--Check for damage spell
if ( data.dmgspell ) and ( data.casting == 0 ) and ( data.cancasttimer == 0 ) then
spelltocast = data.dodmgspell
data.casting , data.cancasttimer = 1
data.casttimer = os.time();
--target all group this needs doing.
spelltarget = highDmgID
casttime1 = data.castimewaitdmg
--Check buff target
if data.targetsingleplayer == true then
casttime1 = data.castimewaitdmg
local hidmgornot = rand_range(1,100);
spelltarget = unit
if hidmgornot <= 95 then
--add 5% chance mob will cast on player that just did dmg
spelltarget = highDmgID
end
end
end
if ( data.debuffspell ) and ( data.casting == 0 ) and ( data.candodebuffspell == 0 ) and ( data.cancasttimer == 0 ) then
spelltocast = data.dodebuffspell
data.casting , data.cancasttimer , data.candodebuffspell = 1
data.casttimer = os.time();
--Check DeBuff Target
if ( data.targetsingleplayer ) then
spelltarget = highDmgID
casttime1 = data.castimewaitdebuff
else
--target all group this needs doing.
spelltarget = highDmgID
end
end
if spelltarget == nil then
spelltarget = highDmgID
end
if spelltocast == nil then
spelltocast = 0
data.casttimer = (os.time() + 1)
LuaAI:DoStartAttack(highDmgID);
end
TEST_Threat_AI_MobModifier(LuaAI);
TEST_Threat_AI.DoCastRandomChance(LuaAI, spelltarget, TEST_Threat_AI.ChanceToCastOnDamage, spelltocast, casttime1)
else
LuaAI:DoStopAttack();
end
local timenow1 = (os.time() + 1)
if data.casttimer == nil then
data.casttimer = os.time();
end
local checkedcasttime = ( timenow1 - data.casttimer )
data.cancasttimer = 0
if checkedcasttime < 6 then
data.cancasttimer = 1
end
LuaAI:SetData(data)
end
function TEST_Threat_AI_OnDeath(_Creature, killer)
LuaAI = _Creature:GetLuaAI()
if LuaAI ~= nil then
TEST_Threat_AI_EraseThreat(LuaAI);
end
end
-- Assign the functions
TEST_Threat_AI.Init = TEST_Threat_AI_Init;
TEST_Threat_AI.MoveInLineOfSight = TEST_Threat_AI_MoveInLineOfSight;
TEST_Threat_AI.AttackStart = TEST_Threat_AI_AttackStart;
TEST_Threat_AI.DamageInflict = TEST_Threat_AI_DamageInflict;
TEST_Threat_AI.Update = TEST_Threat_AI_Update;
TEST_Threat_AI.OnDeath = TEST_Threat_AI_OnDeath;
TEST_Threat_AI.IsVisible = TEST_Threat_AI_IsVisible;
TEST_Threat_AI.MobModifier = TEST_Threat_AI_MobModifier;
TEST_Threat_AI.LevelAggroCheck = TEST_Threat_AI_LevelAggroCheck;
TEST_Threat_AI.AggroDisCheck = TEST_Threat_AI_AggroDisCheck;
-- Link the AI to the different scripts
-- Note: Only the ones who use the Mango.Scripts method of scripting can use TEST_Threat_AI
TEST_Threat_AI:Register('test_threat');