-----------------------------
-- Dr. Azzy's Merc/Homun AI
-- Written by Dr. Azzy of iRO Chaos
-- This AI is intended for use on official servers only
-- Permission granted to distribute in unmodified form.
-- You may expand the AI freely through the M_Extra and H_Extra files
MainVersion="1.54"

ResCmdList			= List.new()
-- As of dev 15, global variables are now in Const_.lua
AutoSkillCooldown		= {}
AutoSkillCooldown[HFLI_MOON]=0
AutoSkillCooldown[HVAN_CAPRICE]=0
AutoSkillCooldown[HVAN_CHAOTIC]=0
AutoSkillCooldown[HLIF_HEAL]=0
AutoSkillCooldown[MH_POISON_MIST]=0
AutoSkillCooldown[MH_LAVA_SLIDE]=0
AutoSkillCooldown[MH_STEINWAND]=0 	
AutoSkillCooldown[MH_SUMMON_LEGION]=0
-----------Config checking----------------

function doInit(myid)
	if IsHomun(myid) == 0 then -- if the stupid devs made GetV(V_MERTYPE,id) work i wouldnt need this!
		MercType=GetMerType(myid)
	end
	if (UseAttackSkill==0 and UseSkillOnly==1) then
		UseSkillOnly = 0
	end
	if DanceMinSP < 0 then
		DanceMinSP=math.floor(GetV(V_MAXSP,MyID)*DanceMinSP/100)*-1
	end
	if ChaseSPPauseSP < 0 then
		ChaseSPPauseSP=math.floor(GetV(V_MAXSP,MyID)*ChaseSPPauseSP/100)*-1
	end
	MyMaxSP=GetV(V_MAXSP,MyID)
	MyLastSP=GetV(V_SP,MyID)
	local loadtimesuccess = pcall(loadtimeouts)
	if loadtimesuccess==false then
		logappend("AAI_ERROR","failed to load timeouts for owner "..GetV(V_OWNER,MyID).." if this is the first time you've used this account with AzzyAI, disregard this message")
	end
	if GetV(V_SKILLATTACKRANGE,myid,HVAN_CAPRICE) > 1 then -- it was a vani
		OldHomunType=VANILMIRTH
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_ERASER_CUTTER) == 1 then
			UseEiraEraseCutter=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_XENO_SLASHER) == 1 then -- 
			UseEiraXenoSlasher=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_STAHL_HORN) == 1 then 
			UseBayeriStahlHorn=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_HEILIGE_STANGE) == 1 then
			UseBayeriHailegeStar=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_NEEDLE_OF_PARALYZE) == 1 then
			UseSeraParalyze=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_POISON_MIST) == 1 then 
			UseSeraPoisonMist=0
	end
	if GetV(V_SKILLATTACKRANGE,myid,MH_LAVA_SLIDE) == 1 then
			UseDieterLavaSlide=0
	end
	if LagReduction ==1 then 
		dofile('./AI/USER_AI/twRO.lua')
	end
	local mskill,mlevel=GetMobSkill(MyID)
	if mskill==0 and (GetV(V_HOMUNTYPE,MyID)==SERA and PoisonMistMode~=0) or (GetV(V_HOMUNTYPE,MyID)==DIETER and LavaSlideMode~=0) then
		mskill,mlevel=GetSightOrAoE(MyID)
	end
	if mskill~=0 and mlevel~=0 and AoEReserveSP==1 and AutoMobMode~=0 then
		ReserveSP=GetSkillInfo(mskill,3,mlevel)
	end
	OnInit()
	if AggressiveRelogTracking~=1 then
		MagTimeout=MagTimeout+500
		SOffensiveTimeout=SOffensiveTimeout+500
		SDefensiveTimeout=SDefensiveTimeout+500
		SOwnerBuffTimeout=SOwnerBuffTimeout+500
		GuardTimeout=GuardTimeout+500
		QuickenTimeout=QuickenTimeout+500
		OffensiveOwnerTimeout	= OffensiveOwnerTimeout+500
		DefensiveOwnerTimeout	= DefensiveOwnerTimeout+500
		OtherOwnerTimeout		= OtherOwnerTimeout+500
	else
		timelag=LastAITime_ART-GetTick()
		MagTimeout=MagTimeout+timelag
		SOffensiveTimeout=SOffensiveTimeout+timelag
		SDefensiveTimeout=SDefensiveTimeout+timelag
		SOwnerBuffTimeout=SOwnerBuffTimeout+timelag
		GuardTimeout=GuardTimeout+timelag
		QuickenTimeout=QuickenTimeout+timelag
		OffensiveOwnerTimeout	= OffensiveOwnerTimeout+timelag
		DefensiveOwnerTimeout	= DefensiveOwnerTimeout+timelag
		OtherOwnerTimeout		= OtherOwnerTimeout+timelag
	end
	AdjustCapriceLevel()
	UpdateTimeoutFile()
	DoneInit=1
end
function AdjustCapriceLevel()
	local msp=GetV(V_MAXSP,MyID)
	if msp < 30 and VanCapriceLevel==nil then 
		if msp >=28 then
			VanCapriceLevel=4
		elseif msp >=26 then
			VanCapriceLevel=3
		elseif msp >=24 then
			VanCapriceLevel=2
		else
			VanCapriceLevel=1
		end
	end
end
function loadtimeouts()
	if IsHomun(MyID)==1 then
		dofile("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua")
	else
		dofile("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua")
	end
	if AggressiveRelogTracking==1 then
		if IsHomun(MyID)==1 then
			dofile(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua")
		else
			dofile(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua")
		end
	end
end

--########################################
--### Friend the merc/homun - old one  ###
--### by Misch, new one by Dr. Azzy    ###
--########################################

if (AssumeHomun==1) then
	if (NewAutoFriend==0) then
		ff = {}
		Hactors = GetActors()
		Howner  = GetV(V_OWNER,Hactors[1])
		FX = 0
		FY = 0
		OX,OY = GetV(V_POSITION,Howner)
		for i,v in ipairs(Hactors) do
			if (v ~= Howner) then
				if (IsMonster(v)==0) then
					FX,FY = GetV(V_POSITION,v)
					if (FX==OX and FY==OY and v<100000) then --is not a player
						MyFriends[v]=2
					end
				end
			end
		end
	else
		NeedToDoAutoFriend=1
		TraceAI("Setting NeedToDoAutoFriend")
	end
end
--####################################
--######## Command Processing ########
--####################################

function	OnMOVE_CMD (x,y)
	TraceAI ("OnMOVE_CMD")
	if GetDistanceAPR(GetV(V_OWNER,MyID),x,y) > 15 or x==0 or y==0 then -- Bogus move command
		local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
		logappend("AAI_ERROR","move command to invalid location "..formatpos(x,y).." owner pos "..formatpos(ox,oy))
		TraceAI("OnMOVE_CMD - Command disregarded; invalid location logged")
		return
	end
	
	ResetCounters()
	if ( x == MyDestX and y == MyDestY and MOTION_MOVE == GetV(V_MOTION,MyID)) then
		return
	end

	--local curX, curY = GetV (V_POSITION,MyID)
	--if (math.abs(x-curX)+math.abs(y-curY) > 15) then
	--	List.pushleft (ResCmdList,{MOVE_CMD,x,y})
	--	x = math.floor((x+curX)/2)  
	--	y = math.floor((y+curY)/2)
	--end
	MyMoveX,MyMoveY=x,y
	MyDestX,MyDestY=x,y
	Move (MyID,MyDestX,MyDestY)	
	MyState = MOVE_CMD_ST
	if (MirAIFriending==1) then --emulate MirAI's annoying friending process
		TraceAI("Starting Friend Routine")
		local actors = GetActors()
		for i,v in ipairs(actors) do
			if (IsMonster(v)~=1 and v~=GetV(V_OWNER,MyID) and v~=MyID) then
				xx,yy=GetV(V_POSITION,v)
				if (xx==x and (yy+1==y or yy-1==y)) then
					
					if (MyFriends[v]==nil) then
						MyFriends[v] = 1
						UpdateFriends()
						MyState=FRIEND_CIRCLE_ST
						TraceAI("Friend Addition")
						NewFriendX,NewFriendY=GetV(V_POSITION,v)
						return
					elseif (MyFriends[v]~=nil) then
						MyFriends[v] = nil
						UpdateFriends()
						MyState=FRIEND_CROSS_ST
						TraceAI("Friend Removal")
						NewFriendX,NewFriendY=GetV(V_POSITION,v)
						return
					end
				end
			end
		end
	end
end


function	OnSTOP_CMD ()

	TraceAI ("OnSTOP_CMD")
	logappend("AAI_ERROR","STOP_CMD sent! This should NEVER HAPPEN!")
	if (GetV(V_MOTION,MyID) ~= MOTION_STAND) then
		Move (MyID,GetV(V_POSITION,MyID))
	end
	MyState = IDLE_ST
	MyDestX = 0
	MyDestY = 0
	MyEnemy = 0
	EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
	EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
	MySkill = 0

end


function	OnATTACK_OBJECT_CMD (id)
	TraceAI ("OnATTACK_OBJECT_CMD")
	ResetCounters()
	MySkill = 0
	MyEnemy = id
	BypassKSProtect=1
	if (UseBerserkAttack==1) then
		BerserkMode=1
	end
	MyState = CHASE_ST
	OnCHASE_ST()
end


function	OnATTACK_AREA_CMD (x,y)

	TraceAI ("OnATTACK_AREA_CMD")
	logappend("AAI_ERROR","ATTACK_AREA_CMD sent! This should NEVER HAPPEN!")
	if (x ~= MyDestX or y ~= MyDestY or MOTION_MOVE ~= GetV(V_MOTION,MyID)) then
		Move (MyID,x,y)	
	end
	MyDestX = x
	MyDestY = y
	MyEnemy = 0
	EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
	EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
	MyState = ATTACK_AREA_CMD_ST
	
end

function	OnPATROL_CMD (x,y)

	TraceAI ("OnPATROL_CMD")
	logappend("AAI_ERROR","PATROL_CMD sent! This should NEVER HAPPEN!")
	MyPatrolX , MyPatrolY = GetV (V_POSITION,MyID)
	MyDestX = x
	MyDestY = y
	Move (MyID,x,y)
	MyState = PATROL_CMD_ST

end


function	OnHOLD_CMD ()

	TraceAI ("OnHOLD_CMD")
	logappend("AAI_ERROR","HOLD_CMD sent! This should NEVER HAPPEN!")
	MyDestX = 0
	MyDestY = 0
	MyEnemy = 0
	EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
	EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
	MyState = HOLD_CMD_ST

end


function	OnSKILL_OBJECT_CMD (level,skill,id)

	TraceAI ("OnSKILL_OBJECT_CMD"..skill.." "..id.." "..level)
	ResetCounters()
	if skill==MH_PAIN_KILLER and IsPlayer(id)==1 and PainkillerFriends~=0 and id~=GetV(V_OWNER,MyID) then
		MyPState = MyState
		MyState = PROVOKE_ST
		MyPEnemy = id
		MyPSkill = skill
		MyPSkillLevel = level
		MyPMode = id
		if MyFriends[id]~=FRIEND then
			MyFriends[id]=PKFRIEND
			if PainkillerFriendsSave==1 then
				UpdateFriends()
			end
		end
		return OnPROVOKE_ST()
	else
		MySkillLevel = level
		MySkill = skill
		MyEnemy = id
		if IsMonster(id)==1 and SuperPassive~=1 then
			BypassKSProtect=1
			if (UseBerserkSkill==1) then
				BerserkMode=1
			end
			MyState = CHASE_ST
			OnCHASE_ST()
		else
			MyState = SKILL_OBJECT_CMD_ST
		end
	end
end


function	OnSKILL_AREA_CMD (level,skill,x,y)
	ResetCounters()
	TraceAI ("OnSKILL_AREA_CMD")
	targetx,targety=Closest(MyID,x,y,AttackRange(MyID,skill,level))
	Move (MyID,targetx,targety)
	MyDestX = x
	MyDestY = y
	MySkillLevel = level
	MySkill = skill
	MyState = SKILL_AREA_CMD_ST
	
end

function	OnFOLLOW_CMD ()
	ReturnToMoveHold = 0 
	if (MyState ~= FOLLOW_CMD_ST) then
		if StickyStandby > 0 then
			ShouldStandby=1
		end
		BetterMoveToOwner (MyID,FollowStayBack)
		MyState = FOLLOW_CMD_ST
		MyEnemy = 0 
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MySkill = 0
		TraceAI ("OnFOLLOW_CMD")
	else
		if StickyStandby > 0 then
			ShouldStandby=0
		end
		MyState = IDLE_ST
		MyEnemy = 0 
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MySkill = 0
		TraceAI ("FOLLOW_CMD_ST --> IDLE_ST")
	end

end




function	ProcessCommand (msg)

	if	(msg[1] == MOVE_CMD) then
		TraceAI ("MOVE_CMD")
		OnMOVE_CMD (msg[2],msg[3])
	elseif	(msg[1] == STOP_CMD) then
		TraceAI ("STOP_CMD")
		OnSTOP_CMD ()
	elseif	(msg[1] == ATTACK_OBJECT_CMD) then
		TraceAI ("ATTACK_OBJECT_CMD")
		OnATTACK_OBJECT_CMD (msg[2])
	elseif	(msg[1] == ATTACK_AREA_CMD) then
		TraceAI ("ATTACK_AREA_CMD")
		OnATTACK_AREA_CMD (msg[2],msg[3])
	elseif	(msg[1] == PATROL_CMD) then
		TraceAI ("PATROL_CMD")
		OnPATROL_CMD (msg[2],msg[3])
	elseif	(msg[1] == HOLD_CMD) then
		TraceAI ("HOLD_CMD")
		OnHOLD_CMD ()
	elseif	(msg[1] == SKILL_OBJECT_CMD) then
		TraceAI ("SKILL_OBJECT_CMD")
		OnSKILL_OBJECT_CMD (msg[2],msg[3],msg[4],msg[5])
	elseif	(msg[1] == SKILL_AREA_CMD) then
		TraceAI ("SKILL_AREA_CMD")
		OnSKILL_AREA_CMD (msg[2],msg[3],msg[4],msg[5])
	elseif	(msg[1] == FOLLOW_CMD) then
		TraceAI ("FOLLOW_CMD")
		OnFOLLOW_CMD ()
	end
end

function ResetCounters()
	MyPState				= 0
	MyPSkill				= 0
	MyPEnemy				= 0
	MyPSkillLevel			= 0
	MySkillUsedCount		= 0
	ChaseGiveUpCount		= 0
	AttackGiveUpCount		= 0
	ChaseDebuffUsed			= 0
	AttackDebuffUsed		= 0
	BypassKSProtect			= 0
	BerserkMode				= 0
	ReturnToState			= 0
	NewFriend				= 0
	FriendMotionTime		= 0
	FriendCircleIter		= 0
	FriendCircleTimeout		= 0
	AtkPosbugFixTimeout		= 0
	SkillObjectCMDTimeout	= 0
	FollowTryCount			= 0
	MyMoveX,MyMoveY			= 0,0
	if CastSkillMode < 0 then
		CastSkill=0
		CastSkillLevel=0
		CastSkillMode=0
	end
	return
end

--###############################
--######## State Process ########
--###############################


function	OnIDLE_ST ()
	--if ReturnToMoveHold~=0 then 
	--	MyState=MOVE_CMD_HOLD_ST
	--	OnMOVE_CMD_HOLD_ST()
	--	return
	--end
	TraceAI ("OnIDLE_ST")
	ResetCounters()
	MySkill					= 0
	MyDestX					= 0
	MyDestY					= 0
	MyEnemy					= 0
	if (DoIdleTasks()==nil) then
		return
	end
	--StickyStandby handling
	if (ShouldStandby==1 and StickyStandby > 0) then
		MyState=FOLLOW_CMD_ST
		return
	end
	--Targeting
	if SuperPassive~=1 then
		local	object = SelectEnemy(GetFriendTargets())
		if (object ~= 0) then							-- MYOWNER_ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return 
		end
		if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0) ) then
			aggro=1
		else
			aggro=0
		end
		object=SelectEnemy(GetEnemyList(MyID,aggro))
		if object~=0 then
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				return OnCHASE_ST()
			end
			return	
		end
		if (aggro==1 and TankMonsterCount < TankMonsterLimit) then
			object = SelectEnemy(GetEnemyList(MyID,-1))
			if (object ~= 0) then
				MyState = TANKCHASE_ST
				MyEnemy = object
				TraceAI ("IDLE_ST -> TANKCHASE_ST")
				return
			end
		end
	end
	--Following
	local distance
	if ReturnToMoveHold ~=0 then
		distance = GetDistanceAP(MyID,StickyX,StickyY)
	else
		distance = GetDistanceFromOwner(MyID)
	end
	if (UseIdleWalk~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP)) then -- CHECK
		if ( distance > GetMoveBounds() or distance == -1) then		-- MYOWNER_OUTSIGNT_IN
			MyState = FOLLOW_ST
			TraceAI ("IDLE_ST -> FOLLOW_ST")
			return
		else 
			TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk)
			MyState=IDLEWALK_ST
		end
	elseif (( distance > DiagonalDist(FollowStayBack+1) and not (ChaseSPPause==1 and GetV(V_SP,MyID) < ChaseSPPauseSP and GetTick() - math.max(LastMovedTime,LastSPTime) > (5000-ChaseSPPauseTime)))or distance == -1) then		-- MYOWNER_OUTSIGNT_IN
		MyState = FOLLOW_ST
		TraceAI ("IDLE_ST -> FOLLOW_ST")
		return OnFOLLOW_ST()
	end	
	if UseAutoHeal==3 then
		if DoHealingTasks(MyID) == 1 then 
			return
		end
	end
	DoAutoBuffs(-2)
	if UseIdleWalk ~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP) then
		TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk)
		MyState=IDLEWALK_ST
	end
end

function	OnFOLLOW_ST ()

	TraceAI ("OnFOLLOW_ST - follow try count: "..FollowTryCount.." ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10))
	local dist = GetDistanceFromOwner(MyID)
	if dist > GetMoveBounds() then 
		ReturnToMoveHold = 0
		StickyX,StickyY=0,0
	end
	if ReturnToMoveHold ~=0 then
		dist = GetDistanceAP(MyID,StickyX,StickyY)
	end
	if (dist <= DiagonalDist(FollowStayBack+1)) then		--  DESTINATION_ARRIVED_IN 
		FollowTryCount=0
		MyState = IDLE_ST
		TraceAI ("FOLLOW_ST -> IDLE_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10))
		if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
			FastChangeCount = FastChangeCount+1
			return OnIDLE_ST()
		end
	else
		if (FollowTryCount > FollowTryPanic and GetV(V_MOTION,MyID)~=MOTION_MOVE) then
			if FollowTryCount > 2*FollowTryPanic then
				if FollowTryCount > 3*FollowTryPanic then 
					TraceAI("FOLLOW_ST -> IDLE_ST - Can't follow even using panic'ed methods - Giving up")
					MyState=IDLE_ST
					if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
						FastChangeCount = FastChangeCount+1
						return OnIDLE_ST()
					end
				else
					if ReturnToMoveHold ==0 then
						MoveToOwner(MyID)
						FollowTryCount=FollowTryCount+1
						TraceAI("Emergency follow - MoveToOwner() called")
					else 
						TraceAI("FOLLOW_ST -> IDLE_ST - Can't get to move sticky location even using panic'ed methods - Giving up")
						MyState=IDLE_ST
						if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
							FastChangeCount = FastChangeCount+1
							return OnIDLE_ST()
						end
					end
				end
			else
				FollowTryCount=FollowTryCount+1
				if ReturnToMoveHold==0 then
					BetterMoveToOwner (MyID,1)
				else
					BetterMoveToLoc(MyID,1,StickyX,StickyY)
				end
			end
		else
			local dx,dy
			if ReturnToMoveHold==0 then
				dx,dy=BetterMoveToOwnerXY(MyID,FollowStayBack)
			else
				dx,dy=BetterMoveToLocXY(MyID,FollowStayBack,StickyX,StickyY)
			end
			local mx,my=GetV(V_POSITION,MyID)
			--TraceAI("followobstacle dest"..formatpos(dx,dy).." pos "..formatpos(mx,my))
			if math.abs(my-dy) <=1 then
				--TraceAI("math.abs(my-dy) <=1")
				if math.abs(mx-dx) <=1 then --We could be in a bounce loop, better see if we are
					--TraceAI("math.abs(my-dy) <=1")
					if MyPosY[1]==my then
						for v=2,5,1 do
							if MyPosX[v]==dx then
								MoveToOwner(MyID)
								FollowTryCount=FollowTryCount+1
								TraceAI("Bounce loop detected, emergency measures taken")
								break
							end
							if v==5 then
								MyDestX,MyDestY=dx,dy
								Move(MyID,MyDestX,MyDestY)
							end
						end
					else
						MyDestX,MyDestY=dx,dy
						Move(MyID,MyDestX,MyDestY)
					end
				else
					--TraceAI("math.abs(mx-dx) > 1"..math.abs(mx-dx))
					MyDestX,MyDestY=dx,dy
					Move(MyID,MyDestX,MyDestY)
				end
			else
				--TraceAI("math.abs(my-dy) > 1"..math.abs(my-dy))
				MyDestX,MyDestY=dx,dy
				Move(MyID,MyDestX,MyDestY)
			end
			if (GetV(V_MOTION,MyID) ~= MOTION_MOVE) then
				FollowTryCount=FollowTryCount+1
			else
				FollowTryCount=0
			end
		end
		TraceAI ("FOLLOW_ST -> FOLLOW_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10).."dest cell"..formatpos(MyDestX,MyDestY))
		return
	end
end


function	OnCHASE_ST ()
	MyAttackStanceX,MyAttackStanceY = 0,0
	TraceAI ("OnCHASE_ST")
	aggro = GetAggroCount()
	if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then
		BerserkMode=1
	end	
	if DoAutoBuffs(-1) == 1 then
		DoAutoBuffs(2)
	end
	if (UseSkillOnly==1 and MySkill ~= 0) then
		skill,level=GetAtkSkill(MyID)
	else
		skill=nil
		level=nil
	end
	if true==IsOutOfSight(MyID,MyEnemy) then
		value="true "
	else
		value="false"
	end
	if(IsNotKS(MyID,MyEnemy)==0) then
		local reason=GetKSReason(MyID,MyEnemy)
		TraceAI ("CHASE_ST -> IDLE_ST : Enemy is taken "..reason)
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MyDestX, MyDestY = 0,0
		ChaseGiveUpCount=0
		if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
			FastChangeCount = FastChangeCount+1
			return OnIDLE_ST()
		end
	end
	if (true == IsOutOfSight(MyID,MyEnemy)) then	-- ENEMY_OUTSIGHT_IN
	
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MyDestX, MyDestY = 0,0
		TraceAI ("CHASE_ST -> IDLE_ST : Enemy out of sight")
		ChaseGiveUpCount=0
		
		if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
			FastChangeCount = FastChangeCount+1
			return OnIDLE_ST()
		else
			return
		end
	end
	if GetV(V_MOTION,MyID)~=MOTION_MOVE then
		if ChaseGiveUpCount > ChaseGiveUp then
			Unreachable[MyEnemy]=1
			if SelectEnemy(GetEnemyList(MyID,-2)) == MyEnemy then --Oh crap, 
				TraceAI("CHASE_ST -> FOLLOW_ST : Target "..MyEnemy.." marked unreachable but is also rescue target! Trying follow state in hopes of a clean line of attack from owner")
				MyState = FOLLOW_ST
				MyEnemy = 0
				EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
				EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
				MyDestX,MyDestY=0,0
		        ChaseGiveUpCount=0
				return OnFOLLOW_ST()
			else
				MyState = IDLE_ST
				MyDestX, MyDestY = 0,0
				TraceAI ("CHASE_ST -> IDLE_ST : Marking target "..MyEnemy.." unreachable")
				ChaseGiveUpCount=0
				MyEnemy = 0
				EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
				EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
				if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
					FastChangeCount = FastChangeCount+1
					return OnIDLE_ST()
				else
					return
				end
			end
		end
		ChaseGiveUpCount=ChaseGiveUpCount+1
	elseif MyEnemies[3]==MyEnemy and GetDistanceAPR(MyEnemy,MyPosX[3],MyPosY[3]) <= GetDistanceAR(MyID,MyEnemy) then
		ChaseGiveUpCount=ChaseGiveUpCount+1
		TraceAI("CHASE_ST: We're not getting any closer - we were "..GetDistanceAPR(MyEnemy,MyPosX[3],MyPosY[3]).." cells away 2 cycles ago, now "..GetDistanceAR(MyID,MyEnemy).." Increment ChaseGiveUpCount")
	end
	OnChaseStart()
	if OpportunisticTargeting ==1 and MySkill==0 and SuperPassive~=1 then
		if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then
			aggro=1
		else
			aggro=0
		end
		object=SelectEnemy(GetEnemyList(MyID,aggro),MyEnemy)
		if object ~= 0 then
			TraceAI("Opportunistic target change - dropping target "..MyEnemy.." for target "..object)
			MyEnemy=object	
			EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
			EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		end
	elseif ((MySkill==MH_TINDER_BREAKER or MySkill==MH_EQC or MySkill==MH_CBC) and EleanorMode==0) or ((MySkill==MH_SONIC_CRAW or MySkill==MH_SILVERVEIN_RUSH or MySkill==MH_MIDNIGHT_FRENZY) and EleanorMode==1) then
		if EleanorDoNotSwitchMode == 1 then 
			TraceAI("Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1")
			logappend("AAI_ERROR","Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1")
		else
			if AutoSkillTimeout < GetTick() then
				DoSkill(MH_STYLE_CHANGE,1,MyID,8)
			end
		end
	end
	if (true == IsInAttackSight(MyID,MyEnemy,skill,level)) then  -- ENEMY_INATTACKSIGHT_IN
		MyState = ATTACK_ST
		AttackTimeout=GetTick()+AttackTimeLimit
		ExChaseGiveUpCount=ChaseGiveUpCount
		ChaseGiveUpCount=0
		MySkillUsedCount=0
		TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN")
		if (FastChangeCount < FastChangeLimit and FastChange_C2A == 1) then
			FastChangeCount = FastChangeCount+1
			return OnATTACK_ST()
		else
			return
		end
	elseif UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then
		dist=GetDistanceA(MyID,MyEnemy)
		local tact_skill,tact_debuff,tact_sp,tact_skillclass=GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy)
		skilltouse={-1,0,0}
		local SkillList=GetTargetedSkills(MyID)
		local availsp = GetV(V_SP,MyID)
		if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then
			availsp = availsp - tact_sp
		end
		TraceAI("Begin autoskill while chasing routine")
		if (tact_skill < 0) then		-- Negative value of TACT_SKILL -> 1 cast of skill
			skill_level=tact_skill*-1	-- with level = to the absolute value of the
			tact_skill=1			-- value of TACT_SKILL.
		else
			skill_level=11
		end
		for i,v in ipairs(SkillList) do

			skilltype=v[1]
			if v[2]~=0 then
				if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then
					if (skilltype == MOB_ATK and UseHomunSSkillChase==1 and AutoMobMode~=0  and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then
						local mobskill_level=skill_level
						if AOEFixedLevel == 1 then
							mobskill_level=v[3]
						end
						local mobmode=0
						if AutoMobMode==2 then
							mobmode=1
						end
						mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode)
						--TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level)))
						if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then
							if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then
								if (skilltouse[1] < 2) then
									skilltouse=v
								end
							end
						end
					elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then
							if (tact_debuff*-1 == v[2] or (tact_debuff==-1 and BasicDebuffs[v[2]]~=nil)) then
								if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
									skilltouse=v
								end
							end
					elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_S) then
						if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
							skilltouse=v
						end
					elseif (skilltype==S_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_OLD) then
						if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
							skilltouse=v
						end
					elseif (skilltype==COMBO_ATK and UseHomunSSkillChase==1 and (AutoComboMode==2 or (AutoComboMode==1 and (tact_skillclass == CLASS_COMBO_1 or tact_skillclass==CLASS_COMBO_2)) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_SILVERVEIN_RUSH or tact_skillclass~=CLASS_COMBO_1)) then
						if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
							skilltouse=v
						end
					elseif (skilltype==GRAPPLE_ATK and UseHomunSSkillChase==1 and (AutoComboMode==2 or (AutoComboMode==1 and tact_skillclass > 5) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_TINDER_BREAKER or (v[2]==MH_CBC and tact_skillclass~=CLASS_GRAPPLE) or tact_skillclass==CLASS_GRAPPLE_2 )) then
						if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
							skilltouse=v
						end
					elseif (skilltype==MINION_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (tact_skillclass==CLASS_MINION or tact_skillclass==CLASS_MIN_OLD or tact_skillclass==CLASS_MIN_S)) then
						if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
							skilltouse=v
						end
					end
				end
			end
		end
		if skilltouse[2]~=0 then
			TraceAI("Using skill while chasing:"..skilltouse[2])
			local slvl=skilltouse[3]
			if skill_level~=11 then
				slvl=skill_level
			end
			DoSkill(skilltouse[2],slvl,MyEnemy)
			if skilltouse[1] == DEBUFF_ATK then
				ChaseDebuffUsed=1
			end
			MySkillUsedCount=1
		end
	else
		TraceAI("Not in range, and can't use chase skill")
	end
	if (GetTact(TACT_CHASE,MyEnemy)~=1) then
		local alt = 0
		if (ChaseGiveUpCount >= 4 and MyPosX[1] == MyPosX[3]  and MyPosY[1]==MyPosY[3]) then
			alt=math.random(2)
			TraceAI("Using alt movement"..alt)
		end
		ex,ey=GetV(V_POSITION,MyEnemy)
		TraceAI("State History: "..MyStates[1].." "..MyStates[2].." "..MyStates[3].." "..MyStates[4].." "..MyStates[5])
		TraceAI("Pos History: "..formatpos(MyPosX[1],MyPosY[1]).." "..formatpos(MyPosX[2],MyPosY[2]).." "..formatpos(MyPosX[3],MyPosY[3]).." "..formatpos(MyPosX[4],MyPosY[4]).." "..formatpos(MyPosX[5],MyPosY[5]))
		TraceAI("Enemy History: "..MyEnemies[1].." "..MyEnemies[2].." "..MyEnemies[3].." "..MyEnemies[4].." "..MyEnemies[5])
		TraceAI("Enemy Pos History: "..formatpos(EnemyPosX[1],EnemyPosY[1]).." "..formatpos(EnemyPosX[2],EnemyPosY[2]).." "..formatpos(EnemyPosX[3],EnemyPosY[3]).." "..formatpos(EnemyPosX[4],EnemyPosY[4]).." "..formatpos(EnemyPosX[5],EnemyPosY[5]))
		TraceAI("current enemy: "..MyEnemy.." "..formatpos(ex,ey))
		if MyStates[1]==CHASE_ST and MyStates[2]==ATTACK_ST and MyStates[3]==CHASE_ST and MyEnemy==MyEnemies[2] and IsPlayer(MyEnemy)~=1 and EnemyPosX[3]==ex and EnemyPosY[3]==ey then
			x,y=AdjustOpp(x,y,ex,ey)
		    Unreachable[MyEnemy]=1
		    TraceAI("CHASE_ST: We transitioned to attack state vs this target 2 cycles ago, now we're chasing it again, and it hasn't moved! Trying to do AdjustOpp, and deprioritizing monster to prevent loop.")
		elseif EnemyPosX[3]~=0 and EnemyPosY[3]~=0 and (ex~=EnemyPosX[3] or ey~=EnemyPosY[3]) and MyEnemy==MyEnemies[3] and alt==0 then
			dx,dy = ex-EnemyPosX[3],ey-EnemyPosY[3]
			r=AttackRange(MyID,MySkill,MySkillLevel)
			x,y = Closest(MyID,ex+dx,ey+dy,r,alt)
			x,y = AdjustStandPoint(x,y,ex,ey,r,alt)
		else
			x,y = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
			if x==-1 or y==-1 then
				if AttackRange(MyID,MySkill,MySkillLevel) < 2 or alt > 0 then 
					MyState = IDLE_ST
					Unreachable[MyEnemy]=1
					MyEnemy = 0
					EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
					EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
					MyDestX, MyDestY = 0,0
					TraceAI ("CHASE_ST -> IDLE_ST : Cannot attack this target, GetStandPoint() reports that all cells around it are occupied.")
					ChaseGiveUpCount=0
					if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
						FastChangeCount = FastChangeCount+1
						return OnIDLE_ST()
					end
				else
					x,y = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,1)
					if x==-1 or y==-1 then
						MyState = IDLE_ST
						Unreachable[MyEnemy]=1
						MyEnemy = 0
						EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
						EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
						MyDestX, MyDestY = 0,0
						TraceAI ("CHASE_ST -> IDLE_ST : Cannot attack this target, GetStandPoint() can't get an unoccupied cell")
						ChaseGiveUpCount=0
						if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
							FastChangeCount = FastChangeCount+1
							return OnIDLE_ST()
						end
					end
				end
			end
		end
		ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
		if GetDistanceAPR(GetV(V_OWNER,MyID),x,y) < GetMoveBounds() then
			if ((x~=MyDestX or y~=MyDestY) or GetV(V_MOTION,MyID)~=MOTION_MOVE)  then
				MyDestX, MyDestY=x,y
				Move (MyID,MyDestX,MyDestY)
				TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN "..MyDestX..","..MyDestY.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y))
			else
				TraceAI("CHASE_ST -> CHASE_ST : Destination not changed "..MyDestX..","..MyDestY.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y))
			end
		else --if ChaseGiveUpCount > 4 then
			MyState = IDLE_ST
			Unreachable[MyEnemy]=1
			MyEnemy = 0
			EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
			EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
			MyDestX, MyDestY = 0,0
			TraceAI ("CHASE_ST -> IDLE_ST : Following enemy would exceed move bounds."..x..","..y.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y))
			ChaseGiveUpCount=0
			if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
				FastChangeCount = FastChangeCount+1
				
				return OnIDLE_ST()
			end
		end
	end
	return
end




function OnATTACK_ST ()
	TraceAI ("OnATTACK_ST MyEnemy: "..MyEnemy.." MyPos "..formatpos(GetV(V_POSITION,MyID)).." ("..GetV(V_MOTION,MyID)..") enemypos "..formatpos(GetV(V_POSITION,MyEnemy)).." ("..GetV(V_MOTION,MyEnemy)..") MyTarget: "..GetV(V_TARGET,MyID))	
	if (true == IsOutOfSight(MyID,MyEnemy)) then -- first thing's first, if enemy is gone drop it. 
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MySkillUseCount= 0
		TraceAI ("ATTACK_ST -> IDLE_ST -- target gone")
		return OnIDLE_ST()
	end
	if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then   -- Enemy dead? Okay we're done here - drop it. 
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MySkillUseCount= 0
		TraceAI ("ATTACK_ST -> IDLE_ST  Enemy dead")
		return OnIDLE_ST()
	end
	local mytarg=GetV(V_TARGET,MyID)
	if mytarg~=MyEnemy and MyStates[1]==ATTACK_ST then
		AttackGiveUpCount=AttackGiveUpCount+1
		if AttackGiveUpCount > 2 then --MyEnemies[3]==MyEnemy and MyStates[3]==ATTACK_ST and MyStates[2]==ATTACK_ST then
			local tx,ty=GetV(V_POSITION,MyEnemy)
			local x,y=GetV(V_POSITION,MyID)
			if AttackGiveUpCount < 5 then
				Move(MyID,tx,ty)
				TraceAI("ATTACK_ST: We've been attacking for 3 cycles, but we still haven't attacked! Something is wrong - Moving to monster cell")
			elseif AttackGiveUpCount < AttackGiveUp then 
				nx,ny=AdjustOpp(x,y,tx,ty)
				Move(MyID,tx,ty)
				TraceAI("ATTACK_ST: We've been attacking for 3 cycles, but we still haven't attacked! Something is wrong - Moving to adjust opposite")
			elseif AttackGiveUpCount > AttackGiveUp and MyEnemies[AttackGiveUp]==MyEnemy and MyStates[AttackGiveUp]==ATTACK_ST then
				MyState = IDLE_ST
				Unreachable[MyEnemy]=1
				MyEnemy = 0
				EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
				EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
				MySkillUseCount= 0
				TraceAI("ATTACK_ST -> IDLE_ST - We've been attacking for 5 cycles, tried moving around, and still haven't attacked it. Marking unreachable")
				return OnIDLE_ST()
			end
		end
	else --We have attacked it successfully
		if GetV(V_MOTION,MyEnemy)==MOTION_DAMAGE then
			local t=1
			for i,v in ipairs(GetActors()) do
				if v~=MyID then
					if GetV(V_TARGET,v)==MyEnemy then
						t=0
						break
					end
				end
			end
			if t==1 then
				AttackTimeout=GetTick()+AttackTimeLimit
				TraceAI("AttackTimeout Reset - we're clearly attacking successfully")
			end
		end
	end
	if (AttackTimeout < GetTick() and AttackTimeLimit > 0) then -- Attack time limit reached.
		MyState = FOLLOW_ST
		Unreachable[MyEnemy]=1
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MySkillUseCount= 0
		TraceAI ("ATTACK_ST -> FOLLOW_ST -- attack timeout reached, so we're probably posbugged. Dropping target and returning to owner in the hope that that sorts it out")
		return OnFOLLOW_ST()
	end
	
	local aggro = GetAggroCount()
	if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then
		BerserkMode=1
	end	
	DoAutoBuffs(2)
	local skill,level
	if UseSkillOnly==1 then
		skill,level=GetAtkSkill(MyID)
	elseif MySkill~=0 then
		skill,level=MySkill,MySkillLevel
	else 
		skill,level=nil,nil
	end
	if (false == IsInAttackSight(MyID,MyEnemy,skill,level)) then  -- Check if we can attack enemy, if not back to chase 
		ResetCounters()
		MyState=CHASE_ST
		TraceAI ("ATTACK_ST -> CHASE_ST  : ENEMY_OUTATTACKSIGHT_IN MyEnemy: "..MyEnemy.." distance to "..GetDistanceA(MyID,MyEnemy))
		if (FastChangeCount < FastChangeLimit and FastChange_A2C == 1) then
			FastChangeCount = FastChangeCount+1
			return OnCHASE_ST()
		end
	end
	if (MyAttackStanceX==0) then
		x,y=GetV(V_POSITION,MyID)
		MyAttackStanceX,MyAttackStanceY=x,y
		logappend("AAI_DANCE","Attack Stance set to "..MyDestX..","..MyDestY.." current pos: "..x..","..y)
	end
	if (UseAutoPushback > 0) then
		if DoAutoPushback(MyID)  == nil then
			return
		end
	end
	OnAttackStart()
	local tact_skill,tact_debuff,tact_sp,tact_skillclass=GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy)
	local skill_level
	
	--logappend("AAI_ATK","tact_skill set "..tact_skill)
	--if AutoMobMode==1 then 
	--	mskill,mlevel=GetMobSkill(MyID)
	--	mobcount=GetMobCount(mskill,mlevel,MyEnemy,1)
	--	--TraceAI("mskill/level "..FormatSkill(mskill,mlevel).." mobcount "..mobcount)
	--elseif AutoMobMode==2 then
	--	mskill,mlevel=GetMobSkill(MyID)
	--	mobcount=GetMobCount(mskill,mlevel,MyEnemy,0)
	--else
	--	mobcount=0
	--end
	--Sniping routine
	if (IsHomun(MyID)==1 and SuperPassive~=1 and BerserkMode==0 and (GetTick() >= AutoSkillTimeout) and aggro <= AutoMobCount and GetTact(TACT_SNIPE,MyEnemy)==SNIPE_OK and (ShouldStandby == 0 or StickyStandby ==0)) then
		target=SelectEnemy(GetEnemyList(MyID,2)) -- This actually checks range - I know it's ugly to do it there, skill range checks need to be done at that point so we can pick a low priority target thats in range, instead of a high priority one out of range. 
		if target ~=0 then
			snipeskill=0
			local snipe_tact_skillclass=GetTact(TACT_SKILLCLASS,target)
			if snipe_tact_skillclass == CLASS_MINION then
				snipeskill,snipelevel=GetMinionSkill(MyID)
			end
			if snipeskill==0 and (snipe_tact_skillclass == CLASS_S or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_S) then
				snipeskill,snipelevel=GetSAtkSkill(MyID)
			end
			if snipeskill==0 and (snipe_tact_skillclass == CLASS_OLD or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_OLD) then
				snipeskill,snipelevel=GetAtkSkill(MyID)
			end
			--TraceAI("snipe "..skill.." level"..level)
			if snipeskill ~=0 then
				slevel = GetTact(TACT_SKILL,target)
				if slevel < 0 then
					slevel=-1*slevel
					if slevel > snipelevel then
						slevel = snipelevel
					end
					if ((GetV(V_SP,MyID)-ReserveSP >= GetTact(TACT_SP,target)+GetSkillInfo(snipeskill,3,slevel))) then
						TraceAI("Snipe attack on "..target.." "..snipeskill.." "..slevel)
						DoSkill(snipeskill,slevel,target)
					end
				end
			end
		end
	end
	
	
	-- Begin skill selection routine
	skilltouse = {-1,0,0}
	-- First digit (1): -1 = no skill, 0 single target, 1 debuff, 2 mob
	-- Second digit (2): skill id
	-- Third digit (3): skill level
	
	if (1==1) then --non paniced attack
		if (MySkill==0 and UseAttackSkill == 1 and GetTick() >= AutoSkillTimeout) then	
			if (tact_skill < 0) then		-- Negative value of TACT_SKILL -> 1 cast of skill
				skill_level=tact_skill*-1	-- with level = to the absolute value of the
				tact_skill=1			-- value of TACT_SKILL.
			else
				skill_level=11
			end
			local SkillList=GetTargetedSkills(MyID)
			TraceAI("Begin autoskill routine")
			local availsp = GetV(V_SP,MyID)
			if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then
				availsp = availsp - tact_sp
			end
			for i,v in ipairs(SkillList) do
				skilltype=v[1]
				TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass"..tact_skillclass.."v"..v[1].." "..v[2].." "..v[3])		
				if v[2]~=0 then
					if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then
						if (skilltype == MOB_ATK and UseHomunSSkillAttack==1 and AutoMobMode~=0 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then
							local mobskill_level=skill_level
							if AOEFixedLevel == 1 then
								mobskill_level=v[3]
							end
							local mobmode=0
							if AutoMobMode==2 then
								mobmode=1
							end
							mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode)
							--TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level)))
							if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then
								if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then
									if (skilltouse[1] < 2) then
										skilltouse=v
									end
								end
							end
						elseif (skilltype ==DEBUFF_ATK and AttackDebuffUsed < AttackDebuffLimit and (IsFriendOrSelf(GetV(V_TARGET,MyEnemy))==1 or AttackDebuffWhenAttacked~=1)) then
							if (tact_debuff == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then
								if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
									skilltouse=v
								end
							end
						elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_S) then
							if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
								skilltouse=v
							end
						elseif (skilltype==S_ATK and UseHomunSSkillAttack==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_OLD) then
							if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
								skilltouse=v
							end
						elseif (skilltype==COMBO_ATK and UseHomunSSkillAttack==1 and (AutoComboMode==2 or (AutoComboMode==1 and (tact_skillclass == CLASS_COMBO_1 or tact_skillclass==CLASS_COMBO_2)) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_SILVERVEIN_RUSH or tact_skillclass~=CLASS_COMBO_1)) then
							if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
								skilltouse=v
							end
						elseif (skilltype==GRAPPLE_ATK and UseHomunSSkillAttack==1 and (AutoComboMode==2 or (AutoComboMode==1 and tact_skillclass > 5) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_TINDER_BREAKER or (v[2]==MH_CBC and tact_skillclass~=CLASS_GRAPPLE) or tact_skillclass==CLASS_GRAPPLE_2 )) then
							if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
								skilltouse=v
							end
						elseif (skilltype==MINION_ATK and UseHomunSSkillAttack==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (tact_skillclass==CLASS_MINION or tact_skillclass==CLASS_MIN_OLD or tact_skillclass==CLASS_MIN_S)) then
							if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
								skilltouse=v
							end
						end
					end
				end
				TraceAI("skill selected "..skilltouse[2])
			end
		end
		-- Now we finalize the selection
		if skilltouse[1]~= -1 then
			MySkill=skilltouse[2]
			if (IsHomun(MyID)==1 and skill_level~=11 and (skilltouse[1]~=MOB_ATK or AOEFixedLevel ~= 1)) then  	--no need to check what skill
				MySkillLevel=skill_level			--Only homuns can use non-max level
			else							--and they dont have any mob/debuffs
				MySkillLevel=skilltouse[3]
			end
			if (skilltouse[1] == DEBUFF_ATK) then
				AttackDebuffUsed=AttackDebuffUsed+1
			else
				MySkillUsedCount=MySkillUsedCount+1
			end
		end	
	end
	
	-- Now we resolve it
	
	--if (MySkill == 0) then
	if (UseSkillOnly ~= 1) then
		Attack (MyID,MyEnemy)
		TraceAI("Normal attack vs: "..MyEnemy)
		if GetV(V_HOMUNTYPE,MyID) == ELEANOR then
			MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0)
			UpdateTimeoutFile()
		end
	end
	-- else
	if (MySkill ~=0) then
		TraceAI("Skill Attack: "..MySkill.." target: "..MyEnemy.." level:"..MySkillLevel)
		SkillTarget=MyEnemy
		if (MySkill==MA_SHARPSHOOTING and AoEMaximizeTargets==1) then
			target,hitcount=GetBestFASTarget(MyID)
			TraceAI(target.." - "..hitcount)
			if hitcount > 0 and target~=-1 then
				SkillTarget=target
			elseif hitcount== 0 then
				MySkill=0
			end
			TraceAI("Skill Attack: "..MySkill.." (FAS) target: "..SkillTarget.." enemy: "..MyEnemy)			
		elseif (MySkill==ML_BRANDISH and AoEMaximizeTargets==1) then
			target=GetBestBrandishTarget(MyID)
			if target~=-1 then
				SkillTarget=target
			end
			TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
		elseif (MySkill==MH_HAILAGE_STAR or MySkill==MS_BOWLING_BASH) and AoEMaximizeTargets==1 then 
			target=GetBestAoETarget(MyID,MySkill,MySkillLevel)
			if target~=-1 then
				SkillTarget=target
			end
			TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
		elseif ((MySkill==MH_XENO_SLASHER or MySkill==MH_LAVA_SLIDE or MySkill==MA_SHOWER or MySkill==MH_POISON_MIST) and AoEMaximizeTargets==1) or  (MySkill==MH_VOLCANIC_ASH and AshMaximizeTargets==1) then
			targx,targy=GetBestAoECoord(MyID,MySkill,MySkillLevel)
			if targx~=-1 then
				SkillTargetX,SkillTargetY=targx,targy
			end
			TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
		end
		if MySkill ~=0 then
			if GetV(V_HOMUNTYPE,MyID) == ELEANOR then
				MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0)
				UpdateTimeoutFile()
			end
			DoSkill(MySkill,MySkillLevel,SkillTarget,-1,SkillTargetX,SkillTargetY)
		end
	end
	if ((UseSkillOnly ~= 1 and UseDanceAttack==1 and GetV(V_SP,MyID) >= DanceMinSP) or (BerserkMode==1 and Berserk_Dance==1) or (panicmode==1 and Panic_UseDanceAttack==1 and HPPercent(MyID) > FleeHP)) and (IsHomun(MyID)==1 and MySkill==0) and GetDistanceRect(MyEnemy,GetV(V_OWNER,MyID)) < 13 then
		nx,ny=GetDanceCell(MyAttackStanceX,MyAttackStanceY,MyEnemy)
		if GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny) >= GetMoveBounds() then
			logappend("AAI_DANCE","Dance attack canceled, too close to move bounds "..GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny).." "..GetMoveBounds())
		else 
			logappend("AAI_DANCE","Dancing between "..MyAttackStanceX..","..MyAttackStanceY.." and "..nx..","..ny)
			Move(MyID,nx,ny)
			Attack(MyID,MyEnemy)
			Move(MyID,MyAttackStanceX,MyAttackStanceY)
		end
	end
	MySkill = 0
	MySkillLevel=0
end


-------------------
-- TANK ROUTINES --
-------------------

function	OnTANKCHASE_ST ()
	if (UseSkillOnly==1) then
		skill,level=GetAtkSkill(MyID)
	elseif (UseSkillOnly==-1) then
		skill,level=GetAtkSkill(MyID)
		if (GetV(V_SP,MyID)-GetTact(TACT_SP,MyEnemy) < GetSkillInfo(skill,3,level)) then
			skill,level,sp=nil,nil,nil
		end
	else
		skill,level,sp=nil,nil,nil
	end
	TraceAI ("OnTANKCHASE_ST")
	if (true == IsOutOfSight(MyID,MyEnemy) or (ChaseGiveUpCount > ChaseGiveUp and GetV(V_MOTION,MyID)~=MOTION_MOVE)) then	-- ENEMY_OUTSIGHT_IN
		if (ChaseGiveUpCount>ChaseGiveUp) then
			Unreachable[MyEnemy]=1
		end
		
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MyDestX, MyDestY = 0,0
		TraceAI ("TANKCHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN")
		ChaseGiveUpCount=0
		
		return
	end
	if (true == IsInAttackSight(MyID,MyEnemy)) then  -- ENEMY_INATTACKSIGHT_IN
		ChaseGiveUpCount=0
		if(IsNotKS(MyID,MyEnemy)==1) then
			MyState = TANK_ST
			MySkillUsedCount=0
			TraceAI ("TANKCHASE_ST -> TANK_ST : ENEMY_INATTACKSIGHT_IN")
			return OnTANK_ST()
		else
			MyState = IDLE_ST
			MyEnemy = 0
			EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
			EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
			MyDestX, MyDestY = 0,0
			TraceAI ("TANKCHASE_ST -> IDLE_ST : Enemy is taken")
		end
		
		return
	end
	TraceAI("Tank chase: Can we skill while chasing?")
	if UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then
		dist=GetDistanceA(MyID,MyEnemy)
		tact_debuff,tact_skill,tact_sp,tact_skillclass=GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy)
		skilltouse={-1,0,0}
		local SkillList=GetTargetedSkills(MyID)
		TraceAI("Begin autoskill while tank chasing routine")
		if (tact_skill < 0) then		-- Negative value of TACT_SKILL -> 1 cast of skill
			skill_level=tact_skill*-1	-- with level = to the absolute value of the
			tact_skill=1			-- value of TACT_SKILL.
		else
			skill_level=11
		end
		for i,v in ipairs(SkillList) do

			skilltype=v[1]
			if v[2]~=0 then
				--logappend("AAI_Chase","skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass ".. tact_skillclass.."v"..v[1].." "..v[2].." "..v[3])
				if (GetV(V_SP,MyID) >= tact_sp+GetSkillInfo(v[2],3,math.min(v[3],skill_level)) and GetSkillInfo(v[2],2,math.min(v[3],skill_level)) >= dist) then 
				--TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.."v"..v[1].." "..v[2].." "..v[3])
					if (skilltype == MOB_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then
						if (tact_skillclass == CLASS_MOB) then
							if (skilltouse[1] < 2) then
								skilltouse=v
							end
						end
					elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then
						if (tact_debuff*-1 == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then
							skilltouse=v
						end
					elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S) then
						skilltouse=v
					elseif (skilltype==S_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD) then
						skilltouse=v
					end
				end
			end
		end
		if skilltouse[2]~=0 then
			TraceAI("Using skill while tank chasing:"..skilltouse[2])
			local slvl=skilltouse[3]
			if skill_level~=11 then
				slvl=skill_level
			end
			DoSkill(skilltouse[2],slvl,MyEnemy,-1)
			if skilltouse[1] == DEBUFF_ATK then
				ChaseDebuffUsed=1
			end
			MySkillUsedCount=1
		end
	else
		TraceAI("Not in range, and can't use chase skill")
	end
	ChaseGiveUpCount=ChaseGiveUpCount+1
	MyDestX, MyDestY =  GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
	if MyDestX==-1 or MyDestY==1 then
		Unreachable[MyEnemy]=1
		MyState = IDLE_ST
		MyEnemy = 0
		EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
		EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		MyDestX, MyDestY = 0,0
		TraceAI ("TANKCHASE_ST -> IDLE_ST : target is surrounded so GetStandPoint can't find valid cell. Target dropped and deprioritized")
		ChaseGiveUpCount=0
	end
	if (GetTact(TACT_CHASE,MyEnemy)~=1) then
		Move (MyID,MyDestX,MyDestY)
		TraceAI ("TANKCHASE_ST -> TANKCHASE_ST : DESTCHANGED_IN"..MyDestX.." "..MyDestY)
	end
	return
end 

function OnTANK_ST()
	if (GetV(V_MOTION,MyEnemy)==MOTION_DEAD or IsOutOfSight(MyID,MyEnemy)) then
		MyState=IDLE_ST
		TraceAI("TANK_ST->IDLE_ST - Target dead or out of sight")
		return
	end
	if (GetV(V_TARGET,MyEnemy)~=MyID and (TankHitTimeout + 1500) < GetTick()) then
		TankHitTimeout = GetTick()
		if (IsInAttackSight(MyID,MyEnemy)==true) then
			Attack(MyID,MyEnemy)
		else
			MyState=TANKCHASE_ST
			TraceAI("TANK_ST->TANKCHASE_ST - Target out of range")
		end
		return
	end
	if GetV(V_TARGET,MyEnemy)==MyID then
		TraceAI("TANK_ST->IDLE_ST - Target is tanked successfully")
		MyState=IDLE_ST
	end
end

--------------------
--- REST ROUTINE ---
--------------------
function	OnREST_ST ()
	TraceAI("OnREST_ST")
	if (DoIdleTasks()==nil) then
		return
	end
	if SuperPassive~=1 then
		local	object = SelectEnemy(GetFriendTargets())
		if (object ~= 0) then		--Check for monsters attacking owner
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("REST_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
			return 
		end
		object = SelectEnemy(GetEnemyList(MyID,0))	
		if (object ~= 0) then
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("REST_ST -> CHASE_ST : ATTACKED_IN")
			return
		end
	end
	
	--If theres nothing else to do, return to the "rest station"
	
	x,y=GetV(V_POSITION,MyID)
	ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
	xoff=x-ox
	yoff=y-oy
	TraceAI(xoff.." "..ox.." "..x)
	if (GetV(V_MOTION,GetV(V_OWNER,MyID))~=MOTION_SIT) then
		MyState=IDLE_ST
		TraceAI("REST_ST -> IDLE_ST: Owner stood up")
	elseif (xoff~=RestXOff or yoff~=RestYOff) then
		MyDestX=ox+RestXOff
		MyDestY=oy+RestYOff
		TraceAI("REST_ST -> REST_ST: moving from "..formatpos(x,y).." to "..formatpos(MyDestX,MyDestY))
		Move(MyID,MyDestX,MyDestY)
	else
		TraceAI("REST_ST -> REST_ST: At rest station")
	end
end

-----------------------------
---Friend motion routines ---
-----------------------------
function	OnFRIEND_CIRCLE_ST()
	x,y = GetV(V_POSITION,MyID)
	dist = GetDistance(x,y,NewFriendX,NewFriendY)
	TraceAI("OnFRIEND_CIRCLE_ST"..x..","..y.." "..dist) 
	if FriendCircleIter == 0 then
		if dist > 2 then
			Move(MyID,NewFriendX,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout > 16 then
				MyState=IDLE_ST
				TraceAI("Dropping circle attempt, timeout exceeded")
			end
			return
		else
			FriendCircleIter=1
		end
	end
	if FriendCircleIter ==1 then
		if x==NewFriendX and y==NewFriendY-2 then
			FriendCircleIter=2
			FriendCircleTimeout=0
		else 
		 	Move(MyID,NewFriendX,NewFriendY-2)
		 	FriendCircleTimeout=FriendCircleTimeout+1
		 	if FriendCircleTimeout>4 then
		 		FriendCircleIter=2
				FriendCircleTimeout=0
		 		TraceAI("giving up on circle step 1")	
		 	end
		 	return
		end
	end
	if FriendCircleIter ==2 then
		if x==NewFriendX-2 and y==NewFriendY then
			FriendCircleIter=3
			FriendCircleTimeout=0
		else 
		 	Move(MyID,NewFriendX-2,NewFriendY)
		 	FriendCircleTimeout=FriendCircleTimeout+1
		 	if FriendCircleTimeout>4 then
		 		FriendCircleIter=3
				FriendCircleTimeout=0
		 		TraceAI("giving up on circle step 2")	
		 	end
		 	return
		end
	end
	if FriendCircleIter ==3 then
		if x==NewFriendX and y==NewFriendY+2 then
			FriendCircleIter=4
			FriendCircleTimeout=0
		else 
			Move(MyID,NewFriendX,NewFriendY+2)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=4
				FriendCircleTimeout=0
				TraceAI("giving up on circle step 3")	
			end
			return
		end
	end
	if FriendCircleIter ==4 then
		if x==NewFriendX+2 and y==NewFriendY then
			FriendCircleIter=5
			FriendCircleTimeout=0
		else 
			Move(MyID,NewFriendX+2,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=5
				FriendCircleTimeout=0
				TraceAI("giving up on circle step 4")	
			end
			return
		end
	end
	if FriendCircleIter == 5 then
		if x==NewFriendX and y==NewFriendY-2 then
			FriendCircleIter=0
			FriendCircleTimeout=0
			MyState=IDLE_ST
			TraceAI("Circle completed")
			return
		else 
			Move(MyID,NewFriendX,NewFriendY-2)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=0
				FriendCircleTimeout=0
				MyState=IDLE_ST
				TraceAI("giving up on circle step 5")	
			end
			return
		end
	end
end

function	OnFRIEND_CROSS_ST()
	x,y = GetV(V_POSITION,MyID)
	dist = GetDistance(x,y,NewFriendX,NewFriendY)
	TraceAI("OnFRIEND_CROSS_ST"..x..","..y.." "..dist) 
	if FriendCircleIter == 0 then
		if dist > 2 then
			Move(MyID,NewFriendX,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout > 16 then
				MyState=IDLE_ST
				TraceAI("Dropping cross attempt, timeout exceeded")
			end
			return
		else
			FriendCircleIter=1
		end
	end
	if FriendCircleIter ==1 then
		if x==NewFriendX-2 and y==NewFriendY then
			FriendCircleIter=2
			FriendCircleTimeout=0
		else 
		 	Move(MyID,NewFriendX-2,NewFriendY)
		 	FriendCircleTimeout=FriendCircleTimeout+1
		 	if FriendCircleTimeout>4 then
		 		FriendCircleIter=2
				FriendCircleTimeout=0
		 		TraceAI("giving up on cross step 1")	
		 	end
		 	return
		end
	end
	if FriendCircleIter ==2 then
		if x==NewFriendX+2 and y==NewFriendY then
			FriendCircleIter=3
			FriendCircleTimeout=0
		else 
		 	Move(MyID,NewFriendX+2,NewFriendY)
		 	FriendCircleTimeout=FriendCircleTimeout+1
		 	if FriendCircleTimeout>4 then
		 		FriendCircleIter=3
				FriendCircleTimeout=0
		 		TraceAI("giving up on cross step 2")	
		 	end
		 	return
		end
	end
	if FriendCircleIter ==3 then
		if x==NewFriendX-2 and y==NewFriendY then
			FriendCircleIter=4
			FriendCircleTimeout=0
		else 
			Move(MyID,NewFriendX-2,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=4
				FriendCircleTimeout=0
				TraceAI("giving up on cross step 3")	
			end
			return
		end
	end
	if FriendCircleIter ==4 then
		if x==NewFriendX+2 and y==NewFriendY then
			FriendCircleIter=5
			FriendCircleTimeout=0
		else 
			Move(MyID,NewFriendX+2,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=5
				FriendCircleTimeout=0
				TraceAI("giving up on cross step 4")	
			end
			return
		end
	end
	if FriendCircleIter == 5 then
		if x==NewFriendX-2 and y==NewFriendY then
			FriendCircleIter=0
			FriendCircleTimeout=0
			MyState=IDLE_ST
			TraceAI("Circle completed")
			return
		else 
			Move(MyID,NewFriendX-2,NewFriendY)
			FriendCircleTimeout=FriendCircleTimeout+1
			if FriendCircleTimeout>4 then
				FriendCircleIter=0
				FriendCircleTimeout=0
				MyState=IDLE_ST
				TraceAI("giving up on cross step 5")	
			end
			return
		end
	end
end

-------------------
--Command State Process
-------------------

function	OnMOVE_CMD_ST ()

	TraceAI ("OnMOVE_CMD_ST")
	if GetDistanceAPR(GetV(V_OWNER,MyID),MyMoveX,MyMoveY) > 15 then
		TraceAI("OnMOVE_CMD_ST -> IDLE_ST: Attempt to move to location off screen")
		logappend("AAI_ERROR","We were in MOVE_CMD_ST trying to move to "..formatpos(MyMoveX,MyMoveY).." while owner standing at "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))))
		MyState=IDLE_ST
		return OnIDLE_ST()
	end
	local x, y = GetV (V_POSITION,MyID)
	if (x == MyMoveX and y == MyMoveY) then	
		StickyX,StickyY=0,0
		if (MoveSticky ~= 0) then
			if (ReturnToMoveHold==0) then
				ReturnToMoveHold = 1
				StickyX,StickyY=x,y
			else 
				ReturnToMoveHold = 0
			end
		end
		TraceAI("OnMOVE_CMD_ST -> IDLE_ST: Arrived at Destination "..formatpos(x,y))
		MyState=IDLE_ST
		return OnIDLE_ST()
	elseif GetV(V_MOTION,MyID) == MOTION_STAND or MyDestX~=MyMoveX or MyDestY~=MyMoveY then
		MyDestX,MyDestY=MyMoveX,MyMoveY
		Move(MyID,MyDestX,MyDestY)
	end
end

function	OnMOVE_CMD_HOLD_ST ()
	
	TraceAI ("OnMOVE_CMD_HOLD_ST")
	MySkillUsedCount		= 0
	ChaseGiveUpCount		= 0
	AttackGiveUpCount		= 0
	ChaseDebuffUsed			= 0
	AttackDebuffUsed		= 0
	BypassKSProtect			= 0
	BerserkMode			= 0
	if (DoIdleTasks()==nil) then
		return
	end
	if (MoveStickyFight==1 and SuperPassive~=1 ) then
		local	object = SelectEnemy(GetFriendTargets())
		if (object ~= 0) then							-- MYOWNER_ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return 
		end
		object = SelectEnemy(GetEnemyList(MyID,0))
		if (object ~= 0) then							-- ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return
		end
		object = SelectEnemy(GetEnemyList(MyID,-1))
		if (object ~= 0) then
			MyState = TANKCHASE_ST
			MyEnemy = object
			TraceAI ("MOVE_CMD_HOLD_ST -> TANKCHASE_ST")
			return
		end
	end
end


function OnSTOP_CMD_ST ()


end




function OnATTACK_OBJECT_CMD_ST ()

	

end


function OnATTACK_AREA_CMD_ST ()

	TraceAI ("OnATTACK_AREA_CMD_ST")

	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID) 
	end

	if (object ~= 0) then							-- MYOWNER_ATTACKED_IN or ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		return
	end

	local x , y = GetV (V_POSITION,MyID)
	if (x == MyDestX and y == MyDestY) then			-- DESTARRIVED_IN
			MyState = IDLE_ST
	end

end




function OnPATROL_CMD_ST ()

	TraceAI ("OnPATROL_CMD_ST")

	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID) 
	end

	if (object ~= 0) then							-- MYOWNER_ATTACKED_IN or ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		TraceAI ("PATROL_CMD_ST -> CHASE_ST : ATTACKED_IN")
		return
	end

	local x , y = GetV (V_POSITION,MyID)
	if (x == MyDestX and y == MyDestY) then			-- DESTARRIVED_IN
		MyDestX = MyPatrolX
		MyDestY = MyPatrolY
		MyPatrolX = x
		MyPatrolY = y
		Move (MyID,MyDestX,MyDestY)
	end

end


-----------------------------------------------------------------------
--IF ANYONE READING MY CODE HAS A FUCK'S CLUE WHAT THE HOLD COMMAND IS
--OR WHAT IN THE DEVIL IT'S PURPOSE IS, PLEASE ENLIGHTEN ME! -Azzy 
----------------------------------------------------------------------

function OnHOLD_CMD_ST () 

	TraceAI ("OnHOLD_CMD_ST")
	logappend("AAI_ERROR","We're in HOLD_CMD_ST - where did hold cmd come from?")
	if (MyEnemy ~= 0) then
		local d = GetDistance(MyEnemy,MyID)
		if (d ~= -1 and d <= GetV(V_ATTACKRANGE,MyID)) then
				Attack (MyID,MyEnemy)
		else
			MyEnemy = 0
			EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
			EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
		end
		return
	end


	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID)
		if (object == 0) then						
			return
		end
	end

	MyEnemy = object

end




function OnSKILL_OBJECT_CMD_ST ()
	if IsInAttackSight(MyID,MyEnemy,MySkill,MySkillLevel) then
		DoSkill(MySkill,MySkillLevel,MyEnemy)
		TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - skill used")
		MyState=IDLE_ST
		MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
		return
	elseif IsOutOfSight(MyID,MyEnemy) then
		TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - target off screen")
		MyState=IDLE_ST
		MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
	elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then
		TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - Couldn't get into range to use skill "..MySkill.." on "..MyEnemy)
		MyState=IDLE_ST
		MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
		return OnIDLE_ST()
	else
		x,y= GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
		if x < 10 and y < 10 then
			local ex,ey=GetV(V_POSITION,MyEnemy)
			local hx,hy=GetV(V_POSITION,MyID)
			local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
			logappend("AAI_ERROR","Anomalous move in progress: h "..formatpos(hx,hy).." "..formatmotion(GetV(V_MOTION,MyID)).." e "..formatpos(ex,ey).." "..formatmotion(GetV(V_MOTION,MyEnemy)).." o "..formatpos(ox,oy).." "..formatmotion(GetV(V_MOTION,GetV(V_OWNER,MyID))).." dest "..formatpos(x,y))
		end
		Move(MyID,x,y)
		SkillObjectCMDTimeout=SkillObjectCMDTimeout+1
		return
	end
	
	
end




function OnSKILL_AREA_CMD_ST ()

	TraceAI ("OnSKILL_AREA_CMD_ST")

	local x , y = GetV (V_POSITION,MyID)
	if (GetDistance(x,y,MyDestX,MyDestY) <= AttackRange(MyID,MySkill,MySkillLevel)) then	-- DESTARRIVED_IN
		DoSkill(MySkill,MySkillLevel,0,nil,MyDestX,MyDestY)
		MyState = IDLE_ST
		MySkill = 0
	else
		targetx,targety=Closest(MyID,MyDestX,MyDestY,AttackRange(MyID,MySkill,MySkillLevel))
		TraceAI("Moving to "..formatpos(targetx,targety).." from "..formatpos(x,y))
		if GetDistanceAPR(GetV(V_OWNER,MyID),targetx,targety) > GetMoveBounds() then
			MyState = IDLE_ST
			MySkill = 0
			TraceAI("OnSKILL_AREA_CMD_ST -> IDLE_ST Target out of range")
		else
			Move (MyID,targetx,targety)
		end
	end

end







function OnFOLLOW_CMD_ST ()
	TraceAI ("OnFOLLOW_CMD_ST")
	local d = GetDistanceA (GetV(V_OWNER,MyID),MyID)
	if ( d > FollowStayBack) then
		BetterMoveToOwner (MyID,FollowStayBack)
		return
	end
	-- Start the friending process
	if (StandbyFriending == 1) then
		local actors = GetActors()
		for i,v in ipairs(actors) do
			if (IsMonster(v)~=1 and GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT) then
				TraceAI("Friend list modification")
				if (IsToRight(GetV(V_OWNER,MyID),v)==1) then
					if (MyFriends[v]==nil) then
						MyFriends[v] = 1
						UpdateFriends()
						MyState=FRIEND_CIRCLE_ST
						ReturnToState=FOLLOW_CMD_ST
						NewFriendX,NewFriendY=GetV(V_POSITION,v)
					end
				elseif (IsToRight(v,GetV(V_OWNER,MyID))==1) then
					if (MyFriends[v]~=nil) then
						MyFriends[v] = nil
						UpdateFriends()
						MyState=FRIEND_CROSS_ST
						ReturnToState=FOLLOW_CMD_ST
						NewFriendX,NewFriendY=GetV(V_POSITION,v)
					end
				end
			end
		end
	end
	-- Okay, that's done. 
	if (DefendStandby == 1 and SuperPassive~=1) then
		local	object = SelectEnemy(GetFriendTargets())
		if (object ~= 0) then							-- MYOWNER_ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return 
		end
		object = SelectEnemy(GetEnemyList(MyID,0))
		if (object ~= 0) then							-- ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : ATTACKED_IN ")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return
		end
	end
end


--#####################################
--### Targeting Routines start here ###
--#####################################
--[[
targets[actorid]=

MotionClass
-1 = Dead
0  = Stand
1  = Moving
2  = Flinch
3  = Attacking
4  = Skilling
5  = Casting
6  = Other

TargetClass
-2  = Non-friend player
-1  = Monster
0  = None
1  = Self
2  = Friend/Owner
]]--]]

function GetFriendTargets() -- returns list of targets of friends who are attacking
	local targets = {}
	for i,v in ipairs (GetActors()) do
		if (IsFriend(v) == 1) then
			motion=GetV(V_MOTION,v)
			target=GetV(V_TARGET,v)
			if (IsMonster(target)==1) then
				tact=GetTact(TACT_BASIC,target)
				if (FriendAttack[motion]==1 and tact > 0 and tact ~=9) then
					targets[target]={MotionClassLU[GetV(V_MOTION,target)],GetTargetClass(target),GetTact(TACT_BASIC,target),GetTact(TACT_CAST,target)}
					
				end
			end
		end
	end
	return targets
end
-- aggro arguments
-- 2 = snipe
-- 1 = aggro
-- 0 = nonaggro
-- -1 = tank
-- -2 = rescue


function	GetEnemyList (myid,aggro)
	local owner  = GetV(V_OWNER,myid)
	local enemys = {}
	if aggro==2 then
		sskill,slevel=GetSAtkSkill(MyID)
		oskill,olevel=GetAtkSkill(MyID)
		mskill,mlevel=GetMinionSkill(MyID)
	end
	TraceAI("GetEnemyList with aggro "..aggro)
	for k,v in pairs(Targets) do
		tact = GetTact(TACT_BASIC,k)
		casttact=GetTact(TACT_CAST,k)
		if tact==TACT_TANKMOB then
			if GetAggroCount() > AutoMobCount then
				tact = TACT_ATTACK_M
			else
				tact = TACT_TANK
			end
		end
		--TraceAI("Target"..k.." tact:"..tact.." Motion"..v[1].." TClass"..v[2])
		if (0 < tact and (tact < 5 or tact >9) and aggro==1 and (DoNotAttackMoving ~=1 or v[1]~=1) and (tact ~= 14 or AttackLastFullSP==0 or SPPercent(MyID)==100)) or  (tact > 9 and tact < 13 and aggro == 2 and k~=MyEnemy) or  (v[2]>0 and tact>0 and (tact~=9 or v[2]==1) and (v[1]==3 or casttact >= CAST_REACT) and aggro~=2 and (aggro > -1 or (aggro==-2 and IsRescueTarget(k)==1))) or (tact == -1 and aggro==-1 and v[2]~=1) then
			--TraceAI("Tactics say to attack:"..k)
			if (IsNotKS(myid,k)==1 and v[1] > -1) then
				--TraceAI("Is alive and not a KS")
				if aggro == 0 or (aggro~=2 and v[2]>0) then 
					if (GetMoveBounds() >= GetDistanceRect(owner,k)) then
						--TraceAI("Adding to target list: "..k)
						r={v[1],v[2],tact,casttact}
						enemys[k] = r
					--else 
					--	tempx,tempy=GetV(V_POSITION,owner)
					--	targx,targy=GetV(V_POSITION,k)
					--	TraceAI("target ignored "..k.." mypos "..tempx..","..tempy.." enemy "..targx..","..targy.." bounds "..GetMoveBounds().."dist "..GetDistanceRect(owner,k))
					end
				elseif aggro~=2 then
					if (GetAggroDist() >= GetDistanceA(owner,k)) then
						TraceAI("Adding to target list: "..k)
						r={v[1],v[2],tact,casttact}
						enemys[k] = r
					end
				else -- Sniping - so we need to check range on skill, instead of aggrodist
					dist = GetDistanceA(MyID,k)
					tact_skill_class=(GetTact(TACT_SKILLCLASS,k))
					if tact_skill_class==CLASS_MINION and mskill~=0 then
						if dist <= GetSkillInfo(mskill,2,mlevel) then
							r={v[1],v[2],tact,casttact}
							enemys[k] = r
						end
					elseif sskill~=0 and tact_skill_class~=CLASS_OLD then
						if dist <= GetSkillInfo(sskill,2,slevel) then
							r={v[1],v[2],tact,casttact}
							enemys[k] = r
						end
					elseif oskill~=0 and tact_skill_class~=CLASS_S then
						if dist <= GetSkillInfo(oskill,2,olevel) then
							r={v[1],v[2],tact,casttact}
							enemys[k] = r
						end
					end
				end
			end
		end
	end
	return enemys
end

-- format of return
-- enemys[n][1] = Motion Class
-- enemys[n][2] = Target Class
-- enemys[n][3] = tact
-- enemys[n][4] = casttact

function SelectEnemy(enemys,curenemy)
	local min_priority=-1
	local priority
	local min_dis = 100
	local dis
	--local min_aggro = -1
	--local aggro = 0
	local result=0
	local max_reachable=1
	--local min_mobcount=1
	--local mobcount=0
	if curenemy~=nil then -- it's an opportunistic attack
		local dist = GetDistanceA(MyID,curenemy)
		local aggrotemp=0
		if IsFriendOrSelf(GetV(V_TARGET,curenemy)) ==1 then
			aggrotemp=1
		end
		min_priority=convpriority(GetTact(TACT_BASIC,curenemy),aggrotemp)
		if dist < 3 then 
			return 0
		else
			min_dis = dist - 3
		end
	end
	for k,v in pairs(enemys) do
		local basepriority = v[3] -- basic tact
		if v[2]>0 and (v[1]==3 or v[4]>=CAST_REACT) then
			aggro=1
		else
			aggro=0
		end
		priority=convpriority(basepriority,aggro)
		--TraceAI(k.." "..basepriority.." "..priority)
		--elseif ((priority==2 or priority==5) and (v[1]==3 or v[4]>=CAST_REACT)) then
		--	aggro=-1
		--elseif then	
		--aggro = 1
		--else
		--	aggro=0
		--end
		dis = GetDistanceA (MyID,k)
		unreachable=Unreachable[k]
		if unreachable == nil then
			unreachable=0
		end
		
		--TraceAI(priority.."/"..min_priority.." "..dis.."/"..min_dis.." "..unreachable.."/"..max_reachable)
		if (unreachable <= max_reachable) then
			--if (aggro >= min_aggro) then
				if (priority > min_priority or (priority==min_priority and dis < min_dis)) then
					--if (dis < min_dis) then
						result = k
						min_dis = dis
						min_priority=priority
						--min_aggro=aggro
						max_reachable=unreachable
					--end
				end
			--end
		end
	end
	--TraceAI("SelectEnemy returning target "..result)
	return result
end
function convpriority(base,agr)
	local priority
	if base > 9 and base < 13 then --Snipe modes are to be treated as attack
		base = base-8
	end
	if base == 13 then
		if agr == 1 then
			base = 7
		else 
			base = 2
		end
	end
	if base == 14 then
		base= 1
	end
	if base>6 and agr==1 then
		priority=base
	elseif base==4 or base==3 or base==15 then
		priority=base+1
		if agr==0 then 
			priority=priority-2
		end
	elseif (priority==5 and aggro==1) then 
		return 2
	elseif base==2 then
		return 1
	else
		return 0
	end
	return priority
end

--####################################################
--### DoIdleTasks - stuff done in any "idle" state ###
--### like buffs and command processing            ###
--####################################################

function DoIdleTasks()
	local cmd = List.popleft(ResCmdList)
	if (cmd ~= nil) then		
		ProcessCommand (cmd)	-- ¿¹¾à ¸í·É¾î Ã³¸® 
		return 
	end
	if OnIdleTasks()==1 then
		return
	end
	if UseAutoHeal==2 then
		if DoHealingTasks(MyID)==1 then
			return
		end
	end
	if DoAutoBuffs(1) ~=1 then
		return
	end
	if (UseSacrificeOwner == 1 and SacrificeTimeout ~=-1) then
		if (GetTick() > SacrificeTimeout) then
			local skill,level= GetSacrificeSkill(MyID)
			if (skill <= 0) then
				SacrificeTimeout = -1
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				DoSkill(skill,level,GetV(V_OWNER,MyID),7)
				SacrificeTimeout = GetTick() + GetSkillInfo(skill,8,level) -- this will recast it before it drops, because the countdown starts at the start of the cast time, but duration counts down from end of cast time.
				return
			end
		end
	end
	if (GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT and MyState~=REST_ST and DoNotUseRest~=1) then
		MyState=REST_ST
		TraceAI("DoIdleTasks - Owner sitting, MyState -> REST_ST")
		return
	end
	return 1
end

function DoAutoBuffs(buffmode)
	if GetTick() < AutoSkillTimeout then
		return 1
	end
	if buffmode==2 then -- Berserk mode only buffs
		if BerserkMode~=1 then
			return 1
		end
	end
	TraceAI("DoAutoBuffs"..buffmode)
	if (UseProvokeOwner == buffmode and ProvokeOwnerTimeout ~=-1) then
		if (GetTick() > ProvokeOwnerTimeout) then
			local skill,level= GetProvokeSkill(MyID)
			if (skill <= 0) then
				ProvokeOwnerTimeout = -1
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["ProvokeOwner"]=GetSkillInfo(skill,3,level)
				if MyASAPBuffs[3]==skill and buffmode==3 then
					ProvokeOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
					logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
				else
					MyPState = MyState
					MyState = PROVOKE_ST
					MyPEnemy = GetV(V_OWNER,MyID)
					MyPSkill = skill
					MyPSkillLevel = level
					MyPMode = 7
					return OnPROVOKE_ST()
				end
			end
		end
	end
	if OffensiveOwnerTimeout ~=-1 then
		TraceAI("offensive owner ~= -1")
		if (GetTick() > OffensiveOwnerTimeout) then
			local skill,level,opt = GetOffensiveOwnerSkill(MyID)
			TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
			if (skill <= 0) then
				OffensiveOwnerTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["OffensiveOwner"]=GetSkillInfo(skill,3,level)
				if MyASAPBuffs[3]==skill and buffmode==3 then
					OffensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
					logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
				else
					MyPState = MyState
					MyState = PROVOKE_ST
					MyPEnemy = GetV(V_OWNER,MyID)
					MyPSkill = skill
					MyPSkillLevel = level
					MyPMode = 11
					return OnPROVOKE_ST()
				end
			end
		end
	end
	if DefensiveOwnerTimeout ~=-1 then
		TraceAI("defensive owner ~= -1")
		if (GetTick() > DefensiveOwnerTimeout)  and (DefensiveBuffOwnerMobbed==0 or (DefensiveBuffOwnerMobbed <= GetAggroCount(GetV(V_OWNER,MyID)) and HPPercent(GetV(V_OWNER,MyID)) < DefensiveBuffOwnerHP)) then
			local skill,level,opt = GetDefensiveOwnerSkill(MyID)
			TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
			if (skill <= 0) then
				DefensiveOwnerTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["DefensiveOwner"]=GetSkillInfo(skill,3,level)
				if MyASAPBuffs[3]==skill and buffmode==3 then
					DefensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
					logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
				else
					MyPState = MyState
					MyState = PROVOKE_ST
					MyPEnemy = GetV(V_OWNER,MyID)
					MyPSkill = skill
					MyPSkillLevel = level
					MyPMode = 12
					return OnPROVOKE_ST()
				end
			end
		end
	end
	if OtherOwnerTimeout ~=-1 then
		TraceAI("other owner ~= -1 "..GetTick().." "..OtherOwnerTimeout)
		if (GetTick() > OtherOwnerTimeout) then
			local skill,level,opt = GetOtherOwnerSkill(MyID)
			TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
			if (skill <= 0) then
				OtherOwnerTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["OtherOwner"]=GetSkillInfo(skill,3,level)
				if MyASAPBuffs[3]==skill and buffmode==3 then
					OtherOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
					logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
				else
					MyPState = MyState
					MyState = PROVOKE_ST
					MyPEnemy = GetV(V_OWNER,MyID)
					MyPSkill = skill
					MyPSkillLevel = level
					MyPMode = 13
					return OnPROVOKE_ST()
				end
			end
		end
	end
	
	if (UseProvokeSelf == buffmode and ProvokeSelfTimeout ~=-1) then
		if (GetTick() > ProvokeSelfTimeout) then
			local skill,level = GetProvokeSkill(MyID)
			if (skill <= 0) then
				ProvokeSelfTimeout = -1
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				--MyState=PROVOKE_ST
				--OnPROVOKE_ST()
				--return
				DoSkill(skill,level,MyID)
				ProvokeSelfTimeout = GetTick()+GetSkillInfo(skill,8,level)
				return
			end
		end
	end
	if (UseOffensiveBuff == buffmode and QuickenTimeout ~=-1) then
		if (GetTick() > QuickenTimeout) then
			local skill,level = GetQuickenSkill(MyID)
			if (skill<=0) then
				QuickenTimeout = -1
			elseif level==0 then
				 -- skill in cooldown
			else
				if (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
					DoSkill(skill,level,MyID,2)
					QuickenTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
					UpdateTimeoutFile()
					return
				end
			end
		end
	end
	
	if (UseDefensiveBuff == buffmode and GuardTimeout ~=-1) then
		if (GetTick() > GuardTimeout) then
			local skill,level = GetGuardSkill(MyID)
			if (skill <= 0) then
				GuardTimeout = -1
			elseif level==0 then
				-- skill in cooldown
			elseif skill==HAMI_BULWARK and UseSmartBulwark ==1  then
				local spreq=MyBuffSPCost+GetSkillInfo(skill,3,level)
				if UseOffensiveBuff ~=0 and QuickenTimeout~=-1 then
					spreq=spreq+120
				end
				if spreq <= GetV(V_SP,MyID) then
					DoSkill(skill,level,MyID,1)
					GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
					UpdateTimeoutFile()
				end
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				DoSkill(skill,level,MyID,1)
				GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
				UpdateTimeoutFile()
				return
			end
		end
	end
	if SOffensiveTimeout ~=-1 then
		if (GetTick() > SOffensiveTimeout) then
			local skill,level,opt = GetSOffensiveSkill(MyID)
			if (skill <= 0) then
				SOffensiveTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["SOffensive"]=GetSkillInfo(skill,3,level)
				DoSkill(skill,level,MyID,4)
				SOffensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)		
				--logappend("AAI_SKILL",skill.."|"..level.."|"..GetSkillInfo(skill,8,level).."|"..SOffensiveTimeout)
				UpdateTimeoutFile()
				return
			end
		end
	end
	if SDefensiveTimeout ~=-1 then
		TraceAI("sdefensive ~= -1")
		if (GetTick() > SDefensiveTimeout) then
			local skill,level,opt = GetSDefensiveSkill(MyID)
			TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
			if (skill <= 0) then
				SDefensiveTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["SDefensive"]=GetSkillInfo(skill,3,level)
				DoSkill(skill,level,MyID,5)
				SDefensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
				UpdateTimeoutFile()
				return
			end
		end
	end
	if SOwnerBuffTimeout ~=-1 then
		if (GetTick() > SOwnerBuffTimeout) then
			local skill,level,opt = GetSOwnerBuffSkill(MyID)
			if (skill <= 0) then
				SOwnerBuffTimeout = -1
			elseif level==0 or opt ~=buffmode then
				-- do nothing, skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["SOwnerBuff"]=GetSkillInfo(skill,3,level)
				DoSkill(skill,level,MyID,6)
				SOwnerBuffTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
				UpdateTimeoutFile()
				return
			end
		end
	end
	if UseBayeriSteinWand == buffmode and SteinWandTimeout~=-1 then
		if GetTick() > SteinWandTimeout  then
			if GetV(V_HOMUNTYPE,MyID)~=BAYERI then
				SteinWandTimeout=-1
			else
				if (GetAggroCount(MyID) >= UseSteinWandSelfMob and UseSteinWandSelfMob~=0) or (GetAggroCount(GetV(V_OWNER,MyID)) >= UseSteinWandOwnerMob and UseSteinWandOwnerMob~=0) then
					DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID,10)
					SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel)
					return
				end
			end
		end
	end
	if (UseAutoMag == buffmode and MagTimeout ~=-1) then
		if (GetTick() > MagTimeout) then
			if (GetV(V_MERTYPE,MyID)~=4) then
				MagTimeout = -1
			elseif (40 <= GetV(V_SP,MyID) and level ~=0) then
				DoSkill(MER_MAGNIFICAT,1,MyID,3)
				MagTimeout = GetTick() + 34000
				UpdateTimeoutFile()
				return
			end
		end
	end
	if SightTimeout ~=-1 then
		TraceAI("tick:"..GetTick().."SightTimeout"..SightTimeout)
		if (GetTick() > SightTimeout) then
			local skill,level,opt = GetSightOrAoE(MyID)
			if (skill <= 0) then
				SightTimeout = -1
			elseif level==0 or opt~=buffmode then
				-- skill in cooldown
			elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
				MyBuffSPCosts["SightOrAoE"]=GetSkillInfo(skill,3,level)
				if IsHomun(MyID)==1 then
					if MyASAPBuffs[3]==skill and buffmode==3 then
						SightTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
						logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
					else
						MyPState = MyState
						MyState = PROVOKE_ST
						MyPEnemy = GetV(V_OWNER,MyID)
						MyPSkill = skill
						MyPSkillLevel = level
						MyPMode = 7
						TraceAI("Using AoE skill as buff"..MyPState.." "..MyState.." "..MyPEnemy.." "..MyPSkill.." "..MyPSkillLevel)
					return OnPROVOKE_ST()
				end				
				else
					DoSkill(skill,level,MyID,7)
					SightTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
					return
				end
			end
		end
	end
	if PainkillerFriends ~=0 then
		local skill,level,opt = GetDefensiveOwnerSkill(MyID)
		opt = PainkillerFriends
		if skill <=0 then 
			--logappend("AAI_PKF","Painkiller friends disabled, don't have skill")
			PainkillerFriends=0
		elseif level==0 or opt~=buffmode then
			--logappend("AAI_PKF","not time to use it")
			-- skill in cooldown
		elseif (GetSkillInfo(skill,3,level) > GetV(V_SP,MyID)) then
			
			--logappend("AAI_PKF","No SP")
			-- no SP
		else
			TraceAI("Painkiller Friends check")
			
			--logappend("AAI_PKF","Painkiller friends check")
			for k,v in pairs(Players) do
				--logappend("AAI_PKF","Painkiller friend "..k)
				if MyFriends[k]==FRIEND or MyFriends[k]==PKFRIEND then
					--logappend("AAI_PKF","Painkiller friend "..k.."not a friend")
					if PKFriendsTimeout[k]~=nil then
						if PKFriendsTimeout[k] < GetTick() then
							MyPState = MyState
							MyState = PROVOKE_ST
							MyPEnemy = k
							MyPSkill = skill
							MyPSkillLevel = level
							MyPMode = k
							return OnPROVOKE_ST()
						end
					else 
						MyPState = MyState
						MyState = PROVOKE_ST
						MyPEnemy = k
						MyPSkill = skill
						MyPSkillLevel = level
						MyPMode = k
						return OnPROVOKE_ST()
					end
				end
			end
		end
	end
	return OnAutoBuffs(buffmode)
end

function DoHealingTasks (myid) 
	rhp=HPPercent(myid)
	ohp=HPPercent(GetV(V_OWNER,myid))
	skill,level=GetHealingSkill(myid)
	if skill<=0 then 
		UseAutoHeal=0
		return
	elseif level==0 or GetTick() < AutoSkillTimeout then
		return --skill in cooldown
	end
	if rhp < HealSelfHP then
		if skill==HVAN_CHAOTIC then
			if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then
				TraceAI("Using self Healing skill "..FormatSkill(skill,level))
				if ohp < HealOwnerHP and MyState==IDLE_ST then
					DoSkill(skill,5,myid) 
				else
					DoSkill(skill,4,myid)
				end
				return 1
			else
				TraceAI("Can't use self healing skill, no SP")
			end
		end
	end
	if ohp < HealOwnerHP then
		if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then
			TraceAI("Using Healing skill "..FormatSkill(skill,level))
			DoSkill(skill,level,GetV(V_OWNER,myid))
			return 1
		else
			TraceAI("Can't use healing skill, no SP")
			return
		end
	end
end

function UpdateTimeoutFile()
	if StickyStandby==2 then
		ShouldStandbyx=ShouldStandby
	else
		ShouldStandbyx=0
	end
	if IsHomun(MyID)==1 then
		OutFile=io.open("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua","w")
	else
		OutFile=io.open("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua","w")
	end
	if OutFile~=nil then
		OutFile:write("MagTimeout="..TimeoutConv(MagTimeout).."\nSOffensiveTimeout="..TimeoutConv(SOffensiveTimeout).."\nSDefensiveTimeout="..TimeoutConv(SDefensiveTimeout).."\nSOwnerBuffTimeout="..TimeoutConv(SOwnerBuffTimeout).."\nGuardTimeout="..TimeoutConv(GuardTimeout).."\nQuickenTimeout="..TimeoutConv(QuickenTimeout).."\nOffensiveOwnerTimeout="..TimeoutConv(OffensiveOwnerTimeout).."\nDefensiveOwnerTimeout="..TimeoutConv(DefensiveOwnerTimeout).."\nOtherOwnerTimeout="..TimeoutConv(OtherOwnerTimeout).."\nShouldStandby="..ShouldStandbyx.."\nRegenTick[1]="..RegenTick[1].."\nMySpheres="..MySpheres.."\nEleanorMode="..EleanorMode)
		OutFile:close()
	else
		TraceAI("Failed to update timeout file")
		logappend("AAI_ERROR","Failed to update timeout file for owner "..GetV(V_OWNER,MyID))
	end
	return
end

function TimeoutConv(a)
	if a==-1 then
		return 0
	else
		return a
	end
end

function OnPROVOKE_ST()
	--local skill,level = GetProvokeSkill(MyID)
	if MyPSkillLevel==nil then
		logappend("AAI_ERROR","PSkillLevel is nil at start of OnPROVOKE_ST")
	end
	if MyPosX[1]==MyPosX[2] and MyPosY[1]==MyPosY[2] then
		SkillObjectCMDTimeout=SkillObjectCMDTimeout+1
	end
	if IsInAttackSight(MyID,MyPEnemy,MyPSkill,MyPSkillLevel) then
		
		if MyPMode == 7 then
			ProvokeOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
		elseif MyPMode== 9 then
			SightTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
		elseif MyPMode== 11 then
			OffensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
		elseif MyPMode== 12 then
			DefensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
		elseif MyPMode== 13 then
			OtherOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
		elseif IsPlayer(MyPMode) then -- to detect painkiller-friends, in which case PMode is the id of the friend
			PKFriendsTimeout[MyPMode]=GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)	
		end
		UpdateTimeoutFile()
		DoSkill(MyPSkill,MyPSkillLevel,MyPEnemy,MyPMode)
		MyState=MyPState
		TraceAI("PROVOKE_ST -> IDLE_ST - Buff/AoE okay"..MyState)
		MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0
		return
	elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then
		TraceAI("PROVOKE_ST -> IDLE_ST Couldn't get into range to provoke/AoE owner")
		if MyPState~=PROVOKE_ST then
			MyState=MyPState
		else
			MyState=0
		end
		SkillObjectCMDTimeout=0
		MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0
		return
	elseif SkillObjectCMDTimeout>(SkillObjectCMDLimit/2) then --We're having trouble getting to the place we want to in order to use this buff, so let's just try to move right to target
		range = AttackRange(MyID,MyPSkill,MyPSkillLevel)
		if range > 2 and MyPEnemy==GetV(V_OWNER,MyID) then
			MoveToOwner(MyID)
		else
			--local x,y=ClosestR(MyID,MyPEnemy,AttackRange(MyID,MyPSkill,MyPSkillLevel),math.random(2))
			local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0)
			Move(MyID,x,y)
		end
		return
	else
		local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0)
		Move(MyID,x,y)
		return
	end
end

function	OnIDLEWALK_ST ()
	TraceAI ("OnIDLEWALK_ST")
	ResetCounters()
	MyEnemy					= 0
	if SPPercent(MyID) < IdleWalkSP then
		MyState=IDLE_ST
		TraceAI ("IDLEWALK_ST -> IDLE_ST : SP is below IdleWalkSP - switching to idle mode to regen SP")
		return OnIDLE_ST()
	end
	if (DoIdleTasks()==nil) then
		return
	end
	--Targeting
	if SuperPassive~=1 then
		local	object = SelectEnemy(GetFriendTargets())
		if (object ~= 0) then							-- MYOWNER_ATTACKED_IN
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("IDLEWALK_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				OnCHASE_ST()
			end
			return 
		end
		if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then
			aggro=1
		else
			aggro=0
		end
		object=SelectEnemy(GetEnemyList(MyID,aggro))
		if object~=0 then
			MyState = CHASE_ST
			MyEnemy = object
			TraceAI ("IDLEWALK_ST -> CHASE_ST : ATTACKED_IN")
			if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
				return OnCHASE_ST()
			end
			return	
		end
		if (aggro==1 and TankMonsterCount < TankMonsterLimit) then
			object = SelectEnemy(GetEnemyList(MyID,-1))
			if (object ~= 0) then
				MyState = TANKCHASE_ST
				MyEnemy = object
				TraceAI ("IDLEWALK_ST -> TANKCHASE_ST")
				return
			end
		end
	end
	--Following
	local distance = GetDistanceAR(MyID,GetV(V_OWNER,MyID))
	if ( distance > GetMoveBounds()) then		-- MYOWNER_OUTSIGNT_IN
		MyState = FOLLOW_ST
		TraceAI ("IDLEWALK_ST -> FOLLOW_ST: Way too far away from owner "..distance)
		return
	end
	if UseAutoHeal==3 then
		if DoHealingTasks(MyID) == 1 then 
			return
		end
	end
	DoAutoBuffs(-2)
	-- end of the usual idle tasks - now we have to do the idle walk part of it, which sucks. 
	local x,y=GetV(V_POSITION,MyID)
	local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
	local motion=GetV(V_MOTION,MyID)
	if (GetDistanceAPR(MyID,MyDestX,MyDestY)>=1) or IdleWalkTries > 6 or GetDistanceAPR(GetV(V_OWNER,MyID),MyDestX,MyDestY) > GetMoveBounds() then 
		if OldHomunType==AMISTR and UseCastleRoute==1 and (UseIdleWalk==5 or UseIdleWalk==6) and RelativeRoute==0 and GetDistanceP(x,y,ox,oy) > 2 then
			if GetTick() > AutoSkillTimeout then
				DoSkill(HAMI_CASTLE,5,MyID)
				TraceAI("Castle Route: Casting castling on self")
			else
				TraceAI("Castle Route: Skill timeout not OK - can't cast castling")
			end
			return
		elseif UseCastleRoute==1 and OldHomunType==AMISTR and RelativeRoute==0 then
			TraceAI("We're set to use castling route, but can't, UseIdleWalk="..UseIdleWalk.." distance: "..GetDistanceP(x,y,ox,oy))
		end
		MyDestX,MyDestY=GetIdleWalkDest(MyID)
		if MyDestX==ox and MyDestY==oy then
			MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1)
		end
		IdleWalkTries=0 
		TraceAI("IDLEWALK_ST: New destination: "..MyDestX..","..MyDestY)
		return Move(MyID,MyDestX,MyDestY)
	elseif motion == MOTION_STAND then
		if IdleWalkTries == 4 then
			MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1)
			TraceAI("IDLEWALK_ST: Having a bit of trouble - adjust dest to: "..MyDestX..","..MyDestY)
		end
		TraceAI("IDLEWALK_ST: No move after "..IdleWalkTries.." trying again:"..MyDestX..","..MyDestY)
		IdleWalkTries=IdleWalkTries+1
		return Move(MyID,MyDestX,MyDestY)
	else
		--we're en route
		return
	end
end

function GetIdleWalkDest(MyID)
	local mx,my=GetV(V_POSITION,MyID)
	local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
	local xoff,yoff=mx-ox,my-oy
	TraceAI("Idlewalk"..xoff..","..yoff)
	if UseIdleWalk==1 then --orbit
		local stepsize = 30
		if IdleWalkDistance > 4 then
			stepsize=30
		else
			stepsize=45
		end
		local temp=math.deg(math.atan2(xoff,yoff))+stepsize/2
		if temp < 0 then
			temp=temp+360
		end
		local step = math.floor(temp/stepsize) + 1
		local angle=math.rad(step*stepsize)
		local destx=math.ceil(math.sin(angle)*IdleWalkDistance)+ox
		local desty=math.ceil(math.cos(angle)*IdleWalkDistance)+oy
		TraceAI("Orbit Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my)
		return destx,desty	
	elseif UseIdleWalk==2 then -- Cross
		local temp=math.deg(math.atan2(xoff,yoff))+45
		if temp < 0 then
			temp=temp+360
		end
		step = math.floor(temp/90)
		TraceAI("Cross step "..step)
		if step == 0 then -- north goes to south
			return ox,oy-IdleWalkDistance
		elseif step == 1 then -- east goes to north
			return ox,oy+IdleWalkDistance
		elseif step == 2 then -- south goes to west
			return ox-IdleWalkDistance,oy
		else  -- must be west!
			return ox+IdleWalkDistance,oy
		end
	elseif UseIdleWalk==3 then -- Rectangle
		local temp=math.deg(math.atan2(xoff,yoff))+22.5
		local tempt=temp
		if temp < 0 then
			temp=temp+360
		end
		local step = math.floor(temp/45) + 1
		local destx,desty = ox,oy
		if step > 0 and step < 4 then
			destx = ox + IdleWalkDistance
		elseif step >4 and step < 8 then
			destx = ox - IdleWalkDistance
		end
		if step ==8 or step == 1 or step == 7 then
			desty= oy + IdleWalkDistance 
		elseif step > 2 and step < 6 then
			desty=oy-IdleWalkDistance
		end
		TraceAI("Rectangle Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." temp: "..temp.." "..tempt.." step: "..step)
		return destx,desty
	elseif UseIdleWalk==4 then -- Random
		local step = math.random(0,359)
		local angle=math.rad(step)
		local destx=absceil(math.sin(angle)*IdleWalkDistance)+ox
		local desty=absceil(math.cos(angle)*IdleWalkDistance)+oy
		TraceAI("Random Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my) 
		return destx,desty	
	elseif UseIdleWalk==5 or UseIdleWalk==6 then -- Route walk
		local routelen=1
		local step = nil
		local dist = 999
		local posx,posy
		if RelativeRoute==1 then
			posx,posy=xoff,yoff
			TraceAI("Relative route "..xoff..","..yoff)	
		else
			TraceAI("Absolute route "..mx..","..my)	
			posx,posy=mx,my
		end
		
		for k,v in pairs(MyRoute) do
			routelen=k
			if v[1]==posx and v[2]==posy then 
				step=k
				dist=0
				TraceAI("Route Analysis: on route cell "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." current step "..step.."/"..dist)
			else 
				local distance=math.sqrt((v[1]-posx)^2+(v[2]-posy))
				if distance < dist then
					dist=distance
					step=k
				end
				TraceAI("Route Analysis: "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." distance "..distance.." current step "..step.."/"..dist)
			end
		end
		-- now we're at position 'step'	
		local nextstep
		if RouteWalkDirection==1 and step==routelen then
			if UseIdleWalk==5 then
				RouteWalkDirection=-1
				nextstep=step-1
			else 
				nextstep=1
			end
		elseif RouteWalkDirection==-1 and step==1 then
			if UseIdleWalk==5 then
				RouteWalkDirection=1
				nextstep=2
			else 
				nextstep=routelen
			end
		else
			nextstep=step+1*RouteWalkDirection
		end
		TraceAI("Route Walk - nextstep "..nextstep.." from "..step)
		local destx,desty
		if RelativeRoute==1 then
			destx,desty = ox+MyRoute[nextstep][1],oy+MyRoute[nextstep][2]
		else
			destx,desty = MyRoute[nextstep][1],MyRoute[nextstep][2]
		end
		
		TraceAI("Route Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." pos: "..posx..","..posy.." routelen: "..routelen.." "..dist.." step: "..step.." nextstep= "..nextstep)
		return destx,desty
	else
		logappend("AAI_ERROR","Invalid UseIdleWalk made it all the way to GetIdleWalkDest()")
		UseIdleWalk=0
		return GetV(V_POSITION,MyID)
	end
end
--######################
--### DoAutoPushback ###
--######################

function DoAutoPushback(myid)
	local actors = GetActors()
	local x,y=GetV(V_POSITION,myid)
	for i,v in ipairs(actors) do
		if (IsMonster(v)==1) then
			local tact= GetTact(TACT_PUSHBACK,v)
			local target =GetV(V_TARGET,v)
			if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then				
				TraceAI("Pushback: "..GetDistanceA(target,v))
				if (GetDistanceA(target,v) <= AutoPushbackThreshold and GetDistanceA(target,v)~=-1) then
					TraceAI("Enemies close to me, using pushback")
					local skill,level=GetPushbackSkill(MyID)
					if (skill<=0) then
						UseAutoPushback=0
					elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level) and GetV(V_SKILLATTACKRANGE,MyID,skill) >= GetDistanceA(myid,v)) then
						DoSkill(skill,level,v)
						return
					end
					break
				end
			end
		elseif (IsFriendOrSelf(id)==0 and PVPmode ~=0) then
			local tact= GetPVPTact(TACT_PUSHBACK,v)
			local target =GetV(V_TARGET,v)
			if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then
				if (GetDistanceA(target,v) <= AutoPushbackThreshold) then
					TraceAI("Enemies close to me, using pushback")
					local skill,level=GetPushbackSkill(MyID)
					if (skill<=0) then
						UseAutoPushback=0
					elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level)) then
						DoSkill(skill,level,v)
						return
					end
					break
				end
			end
		end
	end
	return 1
end


--####################
--### DoKiteAdjust ###
--####################

function DoKiteAdjust(myid,enemy)
	local step
	local target=GetV(V_TARGET,enemy)
	if (IsFriend(target)==1 or target==MyID) then
		step=KiteStep
	else
		step=KiteParanoidStep
	end
	local x,y=GetV(V_POSITION,myid)
	local ox,oy=GetV(V_POSITION,GetV(V_OWNER,myid))
	local ex,ey=GetV(V_POSITION,enemy)
	local xoptions ={[2]=1,[0]=1,[1]=1}
	local yoptions ={[2]=1,[0]=1,[1]=1}
	local xdirection,ydirection=0,0
	if (x > ex) then
		xoptions[2]=0
	elseif (x < ex) then
		xoptions[1]=0
	else
		yoptions[0]=0
	end
	if (y > ey) then
		yoptions[2]=0
	elseif (y < ey) then
		yoptions[1]=0
	else
		xoptions[0]=0
	end
	if (ox > x) then
		if (xoptions[1]==1) then
			xdirection=1
		elseif (xoptions[0]==1) then
			xdirection=0
		elseif (xoptions[2]==1 and (ox-x+step) <= KiteBounds) then
			xdirection=-1
		elseif	(ey < y) then
			xdirection=0
		else
			xdirection=1
		end
	else
		if (xoptions[2]==1) then
			xdirection=-1
		elseif (xoptions[0]==1) then
			xdirection=0
		elseif (xoptions[1]==1 and (x-ox+step) <= KiteBounds) then
			xdirection=1
		elseif	(ey > y) then
			xdirection=0
		else
			xdirection=-1
		end
	end
	if (oy > y) then
		if (yoptions[1]==1) then
			ydirection=1
		elseif (yoptions[0]==1 and xdirection~=0) then
			ydirection=0
		elseif (yoptions[2]==1 and oy-y+step <= step) then
			ydirection=-1
		elseif	(ex > x and xdirection~=0) then
			ydirection=0
		else
			ydirection=1
		end
	else 
		if (yoptions[2]==1) then
			ydirection=-1
		elseif (yoptions[0]==1 and xdirection~=0) then
			ydirection=0
		elseif (yoptions[1]==1 and y-oy+step <= KiteBounds) then
			ydirection=1
		elseif	(ex < x and xdirection~=0) then
			ydirection=0
		else
			ydirection=-1
		end
	end
	TraceAI("Kiteing in "..xdirection..","..ydirection.." direction")
	MyDestX=x+step*xdirection
	MyDestY=y+step*ydirection
	Move(myid,MyDestX,MyDestY)
end

function FailSkillUse(mode) 
	local modex=mode
	if IsPlayer(mode)==1 then
		mode=13
	end
	if SkillFailCount[mode]==nil then 
		SkillFailCount[mode]=0
	end
	if SkillFailCount[mode] < SkillRetryLimit[mode] then
		if mode == -1 then -- attack state
			MySkillUsedCount=math.max(0,MySkillUsedCount-1)
		elseif mode==1 then
			GuardTimeout=1
		elseif mode==2 then
			QuickenTimeout=1
		elseif mode==3 then
			MagTimeout=1
		elseif mode==4 then
			SOffensiveTimeout=1
		elseif mode==5 then
			SDefensiveTimeout=1
		elseif mode==6 then
			SOwnerBuffTimeout=1
		elseif mode==7 then
			SightTimeout=1
		elseif mode==9 then
			ProvokeOwnerTimeout=1
		elseif mode==10 then
			SteinWandTimeout = 1
		elseif mode==11 then
			OffensiveOwnerTimeout = 1
		elseif mode==12 then
			DefensiveOwnerTimeout = 1
		elseif mode==13 then
			OtherOwnerTimeout = 1
		elseif IsPlayer(modex) then
			--logappend("AAI_PKF","skill failed on"..mode)
			PKFriendsTimeout[modex]=1
		else
			OnFailUnknownMode(mode)
		end
		TraceAI("Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again")
		logappend("AAI_SKILLFAIL","Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again")
		SkillFailCount[mode]=SkillFailCount[mode]+1
	else
		TraceAI("Skill cast appears to have failed, but we're past the retry limit, so screw it: Mode "..mode.." fail count "..SkillFailCount[mode])
		logappend("AAI_SKILLFAIL","Skill cast appears to have failed, but we're past the retry limit, so screw it: Mode "..mode.." fail count "..SkillFailCount[mode])
		SkillFailCount[mode]=0
	end
	AutoSkillTimeout = 1
	AutoSkillCastTimeout = 1
	if AutoSkillCooldown[CastSkill]~=nil then
		AutoSkillCooldown[CastSkill]=1
	end
	CastSkill=0
	CastSkillLevel=0
	CastSkillMode=0
	CastSkillTime=0
	CastSkillState=0	
end	

--########################
--### Main AI Function ###
--########################

function AI(myid)
	MyID = myid
	if LastAITime==GetTick() then --prevent AI from running twice in the same tick. 
		TraceAI("double-AI() call detected, blocked")
		return
	else
		if LastAITime + 400 < GetTick() and LastAITime > 10 then
			TraceAI("Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago")
			logappend("AAI_SKILLFAIL", "Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago")
		end
		LastAIDelay=GetTick()-LastAITime
		LastAITime=GetTick()
		if MyEnemy ~= 0 then
			local ex,ey=GetV(V_POSITION,MyEnemy)
			for v=10,2,-1 do
				EnemyPosX[v],EnemyPosY[v]=EnemyPosX[v-1],EnemyPosY[v-1]
			end
			EnemyPosX[1],EnemyPosY[1]=ex,ey,GetV(V_MOTION,MyID)
		end
	end
	if DoneInit== 0 then
		doInit(myid)
	end
	if AggressiveRelogTracking==1 then
		local OutFile
		if IsHomun(MyID)==1 then
			OutFile=io.open(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua","w")
		else
			OutFile=io.open(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua","w")
		end
		if OutFile~=nil then
			OutFile:write("LastAITime_ART="..LastAITime)
			OutFile:close()
		else
			TraceAI("Failed to update time file for Aggressive Relog Tracking")
			logappend("AAI_ERROR","Failed to update aggressive relog time tracking file for owner "..GetV(V_OWNER,MyID))
		end
	end
	OnAIstart()
	-- ###AUTOFRIEND###
	-- Save the ID to a file so counterpart can friend it
	-- Why is it here instead of at the start?
	-- Because the client wont tell us our ID until AI() is called :-(
	if (NeedToDoAutoFriend==1 and NewAutoFriend==1) then
		TraceAI("Now it's time to do the autofriend")
		local owner=GetV(V_OWNER,myid)
		local OutFile
		if (IsHomun(myid)==1) then
			OutFile=io.open("AI/USER_AI/data/H_"..owner..".txt","w")
		else
			OutFile=io.open("AI/USER_AI/data/M_"..owner..".txt","w")
		end
		if OutFile~=nil then
			OutFile:write (myid)
			OutFile:close()
		else
			TraceAI("Failed to create autofriending file.")
			logappend("AAI_ERROR","Failed to create autofriending file for owner "..GetV(V_OWNER,MyID))
		end
		NeedToDoAutoFriend=0
	end
	
	--###BOOKKEEPING###
	
	
	-- Hackjob to fix strange behavior, with the timeouts being set to hours, days, or weeks in the future (suspect due to GetTick() returning bad numbers). 

	if GuardTimeout-GetTick() > 350000 then
		logappend("AAI_ERROR","Guard timeout was "..GuardTimeout.." time is "..GetTick())
		GuardTimeout=1
	end
	if MagTimeout-GetTick() > 350000 then
		logappend("AAI_ERROR","Mag timeout was "..MagTimeout.." time is "..GetTick())
		MagTimeout=1
	end
	if QuickenTimeout-GetTick() > 1205000 then
		logappend("AAI_ERROR","Quicken timeout was "..QuickenTimeout.." time is "..GetTick())
		QuickenTimeout=1
	end
	if SOffensiveTimeout-GetTick() > 350000 then
		logappend("AAI_ERROR","Homun S offensive timeout was "..SOffensiveTimeout.." time is "..GetTick())
		SOffensiveTimeout=1
	end
	if SDefensiveTimeout-GetTick() > 350000 then
		logappend("AAI_ERROR","Homun S defensive timeout was "..SDefensiveTimeout.." time is "..GetTick())
		SDefensiveTimeout=1
	end
	if SOwnerBuffTimeout-GetTick() > 6000000 then
		logappend("AAI_ERROR","Homun S owner buff timeout was "..SOwnerBuffTimeout.." time is "..GetTick())
		SOwnerBuffTimeout=1
	end
	if OffensiveOwnerTimeout-GetTick() > 6000000 then
		logappend("AAI_ERROR","Offensive owner buff timeout was "..OffensiveOwnerTimeout.." time is "..GetTick())
		OffensiveOwnerTimeout=1
	end
	if DefensiveOwnerTimeout-GetTick() > 6000000 then
		logappend("AAI_ERROR","Defensive owner buff timeout was "..DefensiveOwnerTimeout.." time is "..GetTick())
		DefensiveOwnerTimeout=1
	end
	if OtherOwnerTimeout-GetTick() > 6000000 then
		logappend("AAI_ERROR","Other owner buff timeout was "..OtherOwnerTimeout.." time is "..GetTick())
		OtherOwnerTimeout=1
	end
	if MyMaxSP~=GetV(V_MAXSP,MyID) then
		AdjustCapriceLevel()
		MyMaxSP=GetV(V_MAXSP,MyID)
	end
	if AIInitTick==0 or AIInitTick==1 or AIInitTick==nil then
		AIInitTick=GetTick()
	end
	FastChangeCount = 0
	--###For UseSmartBulwark:
	if UseSmartBulwark==1 then
		MyBuffSPCost=0
		for k,v in pairs(MyBuffSPCosts) do
			MyBuffSPCost=MyBuffSPCost+v
		end
	end

	--###DATA GATHERING###
	for k,v in pairs(Unreachable) do
		if (IsOutOfSight(myid,k)==true or GetV(V_MOTION,k)==MOTION_DEAD or (IsFriendOrSelf(GetV(V_TARGET,k))==1) and GetV(V_MOTION,k)~=MOTION_STAND) then
			Unreachable[k]=nil
			TraceAI("Marking as reachable"..k)
		end
	end
	
	-- Position sensing
	local x,y=GetV(V_POSITION,MyID)
	local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
	if MyEnemy ~= 0 then
		local ex,ey=GetV(V_POSITION,MyEnemy)
	else
		ex,ey=0,0
	end
	--myposlog=""
	for v=10,2,-1 do
		MyASAPBuffs[v],MyPosX[v],MyPosY[v],OwnerPosX[v],OwnerPosY[v],EnemyPosX[v],EnemyPosY[v],MyMotions[v],MyStates[v],MyEnemies[v]=MyASAPBuffs[v-1],MyPosX[v-1],MyPosY[v-1],OwnerPosX[v-1],OwnerPosY[v-1],EnemyPosX[v-1],EnemyPosY[v-1],MyMotions[v-1],MyStates[v-1],MyEnemies[v-1]
	end	
	MyPosX[1],MyPosY[1],OwnerPosX[1],OwnerPosY[1],EnemyPosX[1],EnemyPosY[1],MyMotions[1],MyStates[1],MyEnemies[1]=x,y,ox,oy,ex,ey,GetV(V_MOTION,MyID),MyState,MyEnemy
	if MyState==PROVOKE_ST then
		MyASAPBuffs[1]=MyASAPBuffs[2]
	else
		MyASAPBuffs[1]=0
	end
	--for v=1,10 do
	--	myposlog=myposlog.." "..MyPosX[v]..","..MyPosY[v]
	--end
	--TraceAI("MyPosLog "..myposlog.." x="..x.."y="..y)
	if x~=MyPosX[2] or y~=MyPosY[2] then
		--TraceAI("we moved")
		LastMovedTime=GetTick()
	end
	-- Actor list preprocessing
	local actors=GetActors()
	Actors={}
	OldPlayers=Players
	Players={}
	Monsters={}
	Summons={}
	Retainers={}
	Targets={}
	TakenCells={} --OccupiedCellDetection 
	tMobID=""
	TankMonsterCount=0
	local seralegionCount=0
	SeraLegionList=""
	local seralegionActive=0
	local seralegionBugged=0
	for i,v in ipairs(actors) do
		if (MyNoAttack[GetV(V_HOMUNTYPE,v)]~=1 or GetV(V_HOMUNTYPE,MyID)~=SERA) then
			local x,y = GetV(V_POSITION,v)
			TakenCells[x.."_"..y]=1
			if AAIActors[v]~=1 then
				if IsHomun(myid)==1 then
					logappend("AAI_ACTORS","Actor "..v.." type "..GetV(V_HOMUNTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v))
				else
					logappend("AAI_ACTORS","Actor "..v.." mertype "..GetV(V_MERTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v))
				end
				AAIActors[v]=1
			end
			local x,y = GetV(V_POSITION,v)
			if GetV(V_HOMUNTYPE,v)==1102 and x==174 and 33==y and GetV(V_MOTION,v)==MOTION_STAND then
				-- This is the eden group bathory, ignore it. 
			else	
				if (false == IsOutOfSight(myid,v)) then
					Actors[v]=1
					--TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." in sight")
					if GetV(V_HOMUNTYPE,MyID)==SERA then
						local actortype=GetV(V_HOMUNTYPE,v)
						if (actortype > 2157 and actortype < 2161) then
							seralegionCount=seralegionCount+1
							SeraLegionList=SeraLegionList..v.." "
							if GetV(V_MOTION,v)==MOTION_STAND and GetDistanceAR(MyID,v)>3 then
								seralegionBugged=seralegionBugged+1
							else 
								seralegionActive=seralegionActive+1
							end
						end
					end
					if (v >= MagicNumber2 and v <= MagicNumber3) then
						Players[v]=1
						if MyFriends[v]==FRIEND and GetV(V_OWNER,MyID)~=v then --Newly appeared on screen
							if OldPlayers[v]~=1 or AggressiveAutofriend then
								local newfriendhidfile=io.open("./AI/USER_AI/data/H_"..v..".txt","r")
								local newfriendmidfile=io.open("./AI/USER_AI/data/M_"..v..".txt","r")
								TraceAI("new friend on screen, checking H_ID"..v)
								if newfriendhidfile~=nil then
									TraceAI("h_id found"..v)
									local newfriendhid=newfriendhidfile:read("*line")
									if newfriendhid~=nil then
										TraceAI("h_id: "..newfriendhid)
										MyFriends[newfriendhid+1-1]=RETAINER --crude type conversion
									end
									newfriendhidfile:close()
								end
								if newfriendmidfile~=nil then
								TraceAI("new friend on screen, checking M_ID"..v)
									local newfriendmid=newfriendmidfile:read("*line")
									TraceAI("m_id found"..v)
									if newfriendmid~=nil then
										TraceAI("m_id: "..newfriendmid)
										MyFriends[newfriendmid+1-1]=RETAINER
									end
									newfriendmidfile:close()
								end
							end	
						end
					elseif IsMonster(v)==1 then
						--TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." is a monster")
						if LiveMobID == 1 and IsHomun(MyID)==1 then
							tMobID=tMobID.."MobID["..v.."]="..GetV(V_HOMUNTYPE,v).."\n"
						end
						Monsters[v]=1
						if (v >= MagicNumber) then
							Summons[v]=1
						end
						if (AutoDetectPlant==1 and IsActive[v]~=1 and IsHomun(myid)~=1) then
							if (GetV(V_MOTION,v)==MOTION_STAND or GetV(V_MOTION,v)==MOTION_DAMAGE or GetV(V_MOTION,v)==MOTION_DEAD) then
								IsActive[v]=0
							else
								IsActive[v]=1
							end
						end
						if (GetTact(TACT_BASIC,v)==TACT_TANK and GetV(V_TARGET,v)==MyID) then
							TankMonsterCount=TankMonsterCount+1
						end
						motionclass=MotionClassLU[GetV(V_MOTION,v)]
						if motionclass==nil then 
							motionclass=3 
						end --CHANGE
						--TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." "..motionclass)
						if (motionclass~=-1 and (IsActive[v]==1 or AutoDetectPlant~=1 or IsHomun(myid)==1)) then 
							--TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." target added")
							Targets[v]={motionclass,GetTargetClass(GetV(V_TARGET,v))}
						end
					elseif (v >= MagicNumber) then		
						Retainers[v]=1
					end
				end
			end
		end
	end
	if LiveMobID == 1 and IsHomun(MyID)==1 and tMobID ~="" then
		local midoutfile=io.open(AggressiveRelogPath.."MobID.lua","w")
		if midoutfile~=nil then
			midoutfile:write(tMobID)
			midoutfile:close()
		else
			TraceAI("Failed to update MobID - check permissions and/or AggressiveRelogPath.")
			logappend("AAI_ERROR","Failed to update aggressive relog time tracking file for owner "..GetV(V_OWNER,MyID))
		end
	end
	if LiveMobID == 1 and IsHomun(MyID)==0 then
		MobID={}
		if pcall(function () dofile(AggressiveRelogPath.."MobID.lua") end) then
			-- do nothing
		else
			logappend("AAI_ERROR", "Failed to load MobID from "..AggressiveRelogPath.."MobID.lua")
		end
	end
	if GetV(V_HOMUNTYPE,MyID)==SERA then
		for t=1,4 do
			SeraLegionTotalHist[t+1]=SeraLegionTotalHist[t]
			SeraLegionBugHist[t+1]=SeraLegionBugHist[t]
		end
		SeraLegionTotalHist[1]=seralegionCount
		SeraLegionBugHist[1]=seralegionBugged
		SeraLegionCount=math.max(SeraLegionTotalHist[1],SeraLegionTotalHist[2],SeraLegionTotalHist[3],SeraLegionTotalHist[4],SeraLegionTotalHist[5])
		SeraLegionBugged=math.min(SeraLegionBugHist[1],SeraLegionBugHist[2],SeraLegionBugHist[3],SeraLegionBugHist[4],SeraLegionBugHist[5])
		SeraLegionActive=SeraLegionCount-SeraLegionBugged
		if SeraLegionCount==0 and GetTick() > AutoSkillTimeout and GetTick() < AutoSkillCooldown[MH_SUMMON_LEGION] then
			--Why are we in cooldown when there are no bugs out? Cast must have failed, or something killed them. 
			AutoSkillCooldown[MH_SUMMON_LEGION]=1
			TraceAI("Sera Legion: Legion cooldown canceled, bugs not present")
		elseif SeraLegionBugged > 2 then
			AutoSkillCooldown[MH_SUMMON_LEGION]=1
			TraceAI("Sera Legion: Legion cooldown canceled, bugs bugged")
		end
		--logappend("AAI_Legion","Sera Legion - "..SeraLegionCount.." bugs out "..SeraLegionActive.." active and "..SeraLegionBugged.." bugged. "..SeraLegionList)
	end
	if PVPmode==1 then
		for k,v in pairs(Players) do
			--logappend("AAI_PVP",k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
			if IsHomun(MyID)==1 then
				TraceAI("PVP: Player "..k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
			else
				TraceAI("PVP: Player "..k.."  Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
			end
			if IsMonster(k)==1 then
				Targets[k] = {MotionClassLU[GetV(V_MOTION,k)],GetTargetClass(GetV(V_TARGET,k))}
			end
		end
	end
	
	--New autofriend routine
	if (NewAutoFriend==1 and AssumeHomun==1) then
		friendedOK=0
		retainercount=0
		for k,v in pairs(Retainers) do
			if (k~=MyID) then
				retainercount=retainercount+1
				if (MyFriends[k]==2) then
					friendedOK=1
				end
			end
		end	
		if (friendedOK==0) then
			local owner=GetV(V_OWNER,MyID)
			if (IsHomun(myid)==1) then
				InFile=io.open("./AI/USER_AI/data/M_"..owner..".txt","r")
			else
				InFile=io.open("./AI/USER_AI/data/H_"..owner..".txt","r")
			end
			if InFile~=nil then
				retainerid=InFile:read("*a")
				InFile:close()
				retainerid=tonumber(retainerid)
				if (retainerid ~=nil) then
					MyFriends[retainerid]=2
				end
			end
		end
	end
	--SP+Skillfail Watcher
	local clearcastskill=0  --There are a buncha globals that need to be zeroed when we're done watching for skill cast success or failure. However, after the initial round of skill failure checking, we still need to know what this skill was in order to check what the skill was. Also, we need to make sure we don't call FailSkillUse() if it looks like it failed, but the SP for it was used. So we set this to 1 if we see it successfully cast, 2 if failed, and then act appropriately afterwards.
	local sp = GetV(V_SP,MyID)
	if CastSkill~=0 then
		local castskillold = CastSkill
		local castskilloldlvl = CastSkillLevel -- remember these for the SP watcher down below. 
		local skillcastingtime = GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel)*CastTimeRatio
		logappend("AAI_SKILLFAIL", "Skillfail Watcher active: "..FormatSkill(CastSkill,CastSkillLevel).."mode"..CastSkillMode.."delay "..LastAIDelay.." motion "..GetV(V_MOTION,MyID))
		if GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel) == 0 then 
			if LastAIDelay > 500 then
				--Skill cast successfully
				TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
				logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
				clearcastskill=1
				LastASAPTargBuffTime	=0
				LastASAPTargBuffTarg	=0
				LastASAPTargBuffMode	=0
			elseif GetTick() - CastSkillTime > 1000 then
				--Musta failed, we tried to use an instant cast skill 1 second ago and still no sign of it and no sign of delay.
				TraceAI("Delay watcher: no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
				logappend("AAI_SKILLFAIL", "no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
				clearcastskill=2
			end
		else
			if GetV(V_MOTION,MyID)==MOTION_CASTING then
				CastSkillState=1
			elseif CastSkillState>0 then
				if LastAIDelay > 220 then
					--Skill cast successfully
					if CastSkillMode==8 then
						if EleanorMode==1 then
							EleanorMode=0
						else
							EleanorMode=1
						end
						UpdateTimeoutFile()
					end
					TraceAI("Delay watcher: Skill use successful detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
					logappend("AAI_SKILLFAIL","successful skill use detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
					clearcastskill=1
				else
					CastSkillState=CastSkillState+1
					if CastSkillState > 2 then
						local flinch=0
						for v=1,10,1 do
							if MyMotions[v]==MOTION_DAMAGE then
								flinch=1
								break
							end
						end
						-- Ground targeted skills aren't interruptible! 
						if (flinch==1 and GetSkillInfo(CastSkill,7,CastSkillLevel)~=2) or skillcastingtime + AutoSkillTimeout + CastSkillTime < GetTick() then 
							TraceAI("Delay watcher: Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime))
							logappend("AAI_SKILLFAIL", "Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime))
							clearcastskill=2
						elseif skillcastingtime + CastSkillTime > GetTick() then
							TraceAI("Delay watcher: Skill use successful - was seen casting, and no sign of interruption, no delay. mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
							logappend("AAI_SKILLFAIL","Skill use successful - was seen casting, and no sign of interruption, no delay. mode"..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
							clearcastskill=1
						end
					end
				end
			else -- CastSkillState is 0, so the skill hasn't started casting, but just in case it slipped by:
				if LastAIDelay > 220 then
					--Skill cast successfully
					if CastSkillMode==8 then
						if EleanorMode==1 then
							EleanorMode=0
						else
							EleanorMode=1
						end
						UpdateTimeoutFile()
					end
					TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
					logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
					clearcastskill=1
				elseif GetTick() - CastSkillTime > 1000 then
					TraceAI("Delay watcher: no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
					logappend("AAI_SKILLFAIL", "no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
					clearcastskill=2
				end
			end
		end
		-- sp watcher
		if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) or MyLastSP - sp == GetSkillInfo(castskillold,3,castskilloldlvl) then
			--if IsHomun(myid)==0 then
				TraceAI("SP watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
				logappend("AAI_SKILLFAIL","successful skill use detected by SP use - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
				clearcastskill=1
			--end
			if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) then
				LastSPTime=GetTick()
			end
		elseif MyLastSP<= sp then
			if sp-MyLastSP==RegenTick[1] and RegenTick[1]~=0 then
				LastSPTime=GetTick()
			end
		end
	else
		if sp-MyLastSP == RegenTick[1] and RegenTick[1]~=0 then 
			LastSPTime=GetTick()
		elseif sp-MyLastSP > 100 then
			-- do nothing, owner aid-potted it, ignore this
		elseif sp-MyLastSP > RegenTick[1] or (sp-MyLastSP < RegenTick[1] and sp-MyLastSP > 0)  then
			LastSPTime=GetTick()
			if sp-MyLastSP > math.floor(GetV(V_MAXSP,MyID)/100)+3 then -- this could be the correct tick to use
				for v=3,2,-1 do
					RegenTick[2][v]=RegenTick[2][v-1]
				end
				RegenTick[2][1]=sp-MyLastSP
				local tickcount=0
				for k,v in pairs(RegenTick[2]) do
					if v~=sp-MyLastSP then
						break
					end
					tickcount=k
				end
				if tickcount==3 then
					RegenTick[1]=sp-MyLastSP
					TraceAI("Watcher: New SP Regen tick set: "..RegenTick[1])
					logappend("AAI_SKILLFAIL","New SP Regen tick set: "..RegenTick[1])
					UpdateTimeoutFile()
				end
			end
		end
	end
	if clearcastskill==1 then
		SkillFailCount[CastSkillMode]=0
		CastSkill=0
		CastSkillLevel=0
		CastSkillMode=0
		CastSkillTime=0
		CastSkillState=0	
	elseif clearcastskill==2 then
			FailSkillUse(CastSkillMode)
	end
	MyLastSP=sp
	--###COMMAND PROCESSING###
	local msg	= GetMsg (myid)			-- command
	local rmsg	= GetResMsg (myid)		-- reserved command
	
	if msg[1] == NONE_CMD then
		if rmsg[1] ~= NONE_CMD then
			if List.size(ResCmdList) < 10 then
				List.pushright (ResCmdList,rmsg)
			end
		end
	else
		List.clear (ResCmdList)	-- »õ·Î¿î ¸í·ÉÀÌ ÀÔ·ÂµÇ¸é ¿¹¾à ¸í·ÉµéÀº »èÁ¦ÇÑ´Ù.  
		ProcessCommand (msg)	-- ¸í·É¾î Ã³¸® 
	end
	
	
	OnAImiddle()
	
	--###EMERGENCY HANDLING###
	
	-- Avoid Routine
	if (IsHomun(MyID)==1 and UseAvoid==1 and (GetTick()-AIInitTick) > 5000) then
		for i,v in ipairs(actors) do
			if MyAvoid[GetV(V_HOMUNTYPE,v)]==1 then
				os.exit()
			end
		end
	end
	if UseAutoHeal==1 then
		if DoHealingTasks(MyID) == 1 then
			return
		end
	end
	
	-- Prevent from being left behind
	-- Used only in critical (merc out of MoveBounds) situations
	-- Otherwise, IDLE_ST handles it
	
	local dist2owner=GetDistanceRect(MyID,GetV(V_OWNER,MyID))
	if (MyState ~=FOLLOW_ST and dist2owner > GetMoveBounds()) then
		MyState=FOLLOW_ST
	end
	--Cancel all action during the spawn invulnerability
	if (GetTick() < (MyStart + SpawnDelay)) then
		return
	end
	if OldHomunType==AMISTR and UseCastleDefend ==1 then
		if GetTick() > AutoSkillTimeout then
			local mytargetweight =0
			local ownertargetweight =0
			for k,v in pairs(Targets) do 
				if v[2]==1 then
					mytargetweight=mytargetweight+GetTact(TACT_WEIGHT,k)
				elseif v[2]==2 then
					if GetV(V_TARGET,k)==GetV(V_OWNER,MyID) then
						ownertargetweight=ownertargetweight+GetTact(TACT_WEIGHT,k)
					end
				end
			end
			if CastleDefendThreshold <= ownertargetweight and ownertargetweight > mytargetweight then
				DoSkill(HAMI_CASTLE,5,MyID)
			end
		end
	end
	object = SelectEnemy(GetEnemyList(MyID,-2))
	if (object~=0 and object ~= MyEnemy and MyState~=FOLLOW_ST) then
		MyEnemy=object
		MyState=CHASE_ST
		TraceAI("RESCUE ACTIVATED - Targeting "..object)
	end
	-- New in 1.51 - specialized cast react tactics. 
	for k,v in pairs(Targets) do
		if v[2]==1 or v[2] == 2 then
			tactcast= GetTact(TACT_CAST,k)
			if tactcast > CAST_REACT then
				local skill,level=0,0
				if tactcast > 1000 then
					for ii,vv in ipairs(GetTargetedSkills()) do
						if vv[2]==tactcast and vv[3]~=0 and vv[3]~=nil then
							skill=vv[2]
							level=vv[3]
							break
						end
					end
				else -- generic skill response. 
					for ii,vv in ipairs(GetTargetedSkills()) do
						if tactcast==9 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then
							skill=vv[2]
							level=vv[3]
							break
						elseif
							tactcast==vv[1]-10 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then
							skill=vv[2]
							level=vv[3]
							break
						end
					end
				end
				if skill~=0 then
					MySkill=skill
					MySkillLevel=level
					MyEnemy=k
					MyState=OnSKILL_OBJECT_CMD_ST
					TraceAI("CAST_REACT_(skill) being enabled against target"..k.." with tactic "..tactcast.." with "..FormatSkill(skill,level))
					break
				end
			elseif tactcast == CAST_REACT_STIEN then
				if GetV(V_HOMUNTYPE,MyID)==BAYERI then
					if AutoSkillTimeout < GetTick() and GetSkillInfo(MH_STEINWAND,3,5) < GetV(V_SP,MyID) then
						DoSkill(MH_STEINWAND,5,MyID)
						TraceAI("CAST_REACT_STIEN being enabled against target"..k.." with tactic "..tactcast)
					
						return
					end
				end
			elseif tactcast == CAST_REACT_CASTLE then
				if (modulo(GetV(V_HOMUNTYPE,MyID),4)==AMISTR or OldHomunType==AMISTR) and v[2]==2 then
					if AutoSkillTimeout < GetTick() and GetSkillInfo(HAMI_CASTLE,3,5) < GetV(V_SP,MyID) then
						DoSkill(HAMI_CASTLE,5,MyID)
						TraceAI("CAST_REACT_CASTLE being enabled against target"..k.." with tactic "..tactcast)
						return
					end
				end
			end
		end
	end

	if MyState~=PROVOKE_ST then
		if DoAutoBuffs(3) ~= 1 then
			logappend("AAI_ASAP","ASAP BUFF "..MyState)
			if MyState==PROVOKE_ST then --if we're looking at a targeted buff, this means it's an ASAP poison mist, painkiller, or lava slide
				MyASAPBuffs[1]=MyPSkill
			end
			return
		end
	end
	-- Check to see if mercenary should activate kiting routine
	if ((KiteMonsters==1 and (KiteOK(MyID)==1 or ForceKite == 1)) or HPPercent(MyID) < FleeHP) then
		local x,y=GetV(V_POSITION,MyID)
		if ((x==KiteDestX and y==KiteDestY) or (KiteDestX==0 and KiteDestY==0)) then
			local actors = GetActors()
			kitecalled=0
			for i,v in ipairs(actors) do
				if (IsMonster(v)==1 and GetV(V_MOTION,v) ~= MOTION_DEAD) then
					local target=GetV(V_TARGET,v)
					local tact = GetTact(TACT_KITE,v)
					if ((IsFriendOrSelf(target)==1 and tact==1) or tact==2) then
						local threshold
						if (IsFriend(target)==1 or target==MyID) then
							threshold=KiteThreshold
						else
							threshold=KiteParanoidThreshold
						end
						if (GetDistanceA(MyID,v) <= threshold) then
							TraceAI("Enemies close to me, start kite routine. ")
							DoKiteAdjust(MyID,v)
							kitecalled=1
							break
						end
					end
				end
			end
			if (kitecalled == 0) then
				KiteDestX,KiteDestY=0,0
			end
		else
			Move(myid,KiteDestX,KiteDestY)
		end
	end 
	--Stienwand autocast on tele stuff
	if GetTick() < SteinWandPauseTime then
		return
	end
	if UseSteinWandTele ==1 and GetV(V_HOMUNTYPE,MyID)==BAYERI then
		if SteinWandPauseTime == 0 then
			DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID)
			SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel)
			SteinWandPauseTime=SteinWandTelePause+GetTick()
			return
		end
	end
	--###STATE PROCESSES###
	--TraceAI("SP tracking: Time: "..GetTick().." last moved: "..LastMovedTime.." last sp time "..LastSPTime)
 	if (MyState == IDLE_ST) then
		OnIDLE_ST ()
	elseif (MyState == CHASE_ST) then					
		OnCHASE_ST ()
	elseif (MyState == ATTACK_ST) then
		OnATTACK_ST ()
	elseif (MyState == FOLLOW_ST) then
		OnFOLLOW_ST ()
	elseif (MyState == MOVE_CMD_ST) then
		OnMOVE_CMD_ST ()
	elseif (MyState == STOP_CMD_ST) then
		OnSTOP_CMD_ST ()
	elseif (MyState == ATTACK_OBJECT_CMD_ST) then
		OnATTACK_OBJECT_CMD_ST ()
	elseif (MyState == ATTACK_AREA_CMD_ST) then
		OnATTACK_AREA_CMD_ST ()
	elseif (MyState == PATROL_CMD_ST) then
		OnPATROL_CMD_ST ()
	elseif (MyState == HOLD_CMD_ST) then
		OnHOLD_CMD_ST ()
	elseif (MyState == SKILL_OBJECT_CMD_ST) then
		OnSKILL_OBJECT_CMD_ST ()
	elseif (MyState == SKILL_AREA_CMD_ST) then
		OnSKILL_AREA_CMD_ST ()
	elseif (MyState == FOLLOW_CMD_ST) then
		OnFOLLOW_CMD_ST ()
	elseif (MyState == IDLEWALK_ST) then
		OnIDLEWALK_ST ()
	elseif (MyState == ORBITWALK_ST) then
		OnORBITWALK_ST()
	elseif (MyState == REST_ST) then
		OnREST_ST()
	elseif (MyState == TANKCHASE_ST) then
		OnTANKCHASE_ST()
	elseif (MyState == TANK_ST) then
		OnTANK_ST()
	elseif (MyState == PROVOKE_ST) then
		OnPROVOKE_ST()
	elseif (MyState == MOVE_CMD_HOLD_ST) then
		OnMOVE_CMD_HOLD_ST()
	elseif (MyState == FRIEND_CROSS_ST) then
		OnFRIEND_CROSS_ST()
	elseif (MyState == FRIEND_CIRCLE_ST) then
		OnFRIEND_CIRCLE_ST()
	else
		if NewState(MyState)==-1 then  
			TraceAI("Invalid State: "..MyState.." -> IDLE_ST")
			logappend("AAI_ERROR","MyState set to invalid state: "..MyState)
			MyState=IDLE_ST
		end
	end
	OnAIEnd()
end