All pastes #464100 Raw Edit

Miscellany

public text v1 · immutable
#464100 ·published 2007-04-29 10:03 UTC
rendered paste body
--
--      $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');