Advertisement
Guest User

AI_main.lua

a guest
Mar 23rd, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 125.90 KB | None | 0 0
  1. -----------------------------
  2. -- Dr. Azzy's Merc/Homun AI
  3. -- Written by Dr. Azzy of iRO Chaos
  4. -- This AI is intended for use on official servers only
  5. -- Permission granted to distribute in unmodified form.
  6. -- You may expand the AI freely through the M_Extra and H_Extra files
  7. MainVersion="1.54"
  8.  
  9. ResCmdList          = List.new()
  10. -- As of dev 15, global variables are now in Const_.lua
  11. AutoSkillCooldown       = {}
  12. AutoSkillCooldown[HFLI_MOON]=0
  13. AutoSkillCooldown[HVAN_CAPRICE]=0
  14. AutoSkillCooldown[HVAN_CHAOTIC]=0
  15. AutoSkillCooldown[HLIF_HEAL]=0
  16. AutoSkillCooldown[MH_POISON_MIST]=0
  17. AutoSkillCooldown[MH_LAVA_SLIDE]=0
  18. AutoSkillCooldown[MH_STEINWAND]=0  
  19. AutoSkillCooldown[MH_SUMMON_LEGION]=0
  20. -----------Config checking----------------
  21.  
  22. function doInit(myid)
  23.     if IsHomun(myid) == 0 then -- if the stupid devs made GetV(V_MERTYPE,id) work i wouldnt need this!
  24.         MercType=GetMerType(myid)
  25.     end
  26.     if (UseAttackSkill==0 and UseSkillOnly==1) then
  27.         UseSkillOnly = 0
  28.     end
  29.     if DanceMinSP < 0 then
  30.         DanceMinSP=math.floor(GetV(V_MAXSP,MyID)*DanceMinSP/100)*-1
  31.     end
  32.     if ChaseSPPauseSP < 0 then
  33.         ChaseSPPauseSP=math.floor(GetV(V_MAXSP,MyID)*ChaseSPPauseSP/100)*-1
  34.     end
  35.     MyMaxSP=GetV(V_MAXSP,MyID)
  36.     MyLastSP=GetV(V_SP,MyID)
  37.     local loadtimesuccess = pcall(loadtimeouts)
  38.     if loadtimesuccess==false then
  39.         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")
  40.     end
  41.     if GetV(V_SKILLATTACKRANGE,myid,HVAN_CAPRICE) > 1 then -- it was a vani
  42.         OldHomunType=VANILMIRTH
  43.     end
  44.     if GetV(V_SKILLATTACKRANGE,myid,MH_ERASER_CUTTER) == 1 then
  45.             UseEiraEraseCutter=0
  46.     end
  47.     if GetV(V_SKILLATTACKRANGE,myid,MH_XENO_SLASHER) == 1 then --
  48.             UseEiraXenoSlasher=0
  49.     end
  50.     if GetV(V_SKILLATTACKRANGE,myid,MH_STAHL_HORN) == 1 then
  51.             UseBayeriStahlHorn=0
  52.     end
  53.     if GetV(V_SKILLATTACKRANGE,myid,MH_HEILIGE_STANGE) == 1 then
  54.             UseBayeriHailegeStar=0
  55.     end
  56.     if GetV(V_SKILLATTACKRANGE,myid,MH_NEEDLE_OF_PARALYZE) == 1 then
  57.             UseSeraParalyze=0
  58.     end
  59.     if GetV(V_SKILLATTACKRANGE,myid,MH_POISON_MIST) == 1 then
  60.             UseSeraPoisonMist=0
  61.     end
  62.     if GetV(V_SKILLATTACKRANGE,myid,MH_LAVA_SLIDE) == 1 then
  63.             UseDieterLavaSlide=0
  64.     end
  65.     if LagReduction ==1 then
  66.         dofile('./AI/USER_AI/twRO.lua')
  67.     end
  68.     local mskill,mlevel=GetMobSkill(MyID)
  69.     if mskill==0 and (GetV(V_HOMUNTYPE,MyID)==SERA and PoisonMistMode~=0) or (GetV(V_HOMUNTYPE,MyID)==DIETER and LavaSlideMode~=0) then
  70.         mskill,mlevel=GetSightOrAoE(MyID)
  71.     end
  72.     if mskill~=0 and mlevel~=0 and AoEReserveSP==1 and AutoMobMode~=0 then
  73.         ReserveSP=GetSkillInfo(mskill,3,mlevel)
  74.     end
  75.     OnInit()
  76.     if AggressiveRelogTracking~=1 then
  77.         MagTimeout=MagTimeout+500
  78.         SOffensiveTimeout=SOffensiveTimeout+500
  79.         SDefensiveTimeout=SDefensiveTimeout+500
  80.         SOwnerBuffTimeout=SOwnerBuffTimeout+500
  81.         GuardTimeout=GuardTimeout+500
  82.         QuickenTimeout=QuickenTimeout+500
  83.         OffensiveOwnerTimeout   = OffensiveOwnerTimeout+500
  84.         DefensiveOwnerTimeout   = DefensiveOwnerTimeout+500
  85.         OtherOwnerTimeout       = OtherOwnerTimeout+500
  86.     else
  87.         timelag=LastAITime_ART-GetTick()
  88.         MagTimeout=MagTimeout+timelag
  89.         SOffensiveTimeout=SOffensiveTimeout+timelag
  90.         SDefensiveTimeout=SDefensiveTimeout+timelag
  91.         SOwnerBuffTimeout=SOwnerBuffTimeout+timelag
  92.         GuardTimeout=GuardTimeout+timelag
  93.         QuickenTimeout=QuickenTimeout+timelag
  94.         OffensiveOwnerTimeout   = OffensiveOwnerTimeout+timelag
  95.         DefensiveOwnerTimeout   = DefensiveOwnerTimeout+timelag
  96.         OtherOwnerTimeout       = OtherOwnerTimeout+timelag
  97.     end
  98.     AdjustCapriceLevel()
  99.     UpdateTimeoutFile()
  100.     DoneInit=1
  101. end
  102. function AdjustCapriceLevel()
  103.     local msp=GetV(V_MAXSP,MyID)
  104.     if msp < 30 and VanCapriceLevel==nil then
  105.         if msp >=28 then
  106.             VanCapriceLevel=4
  107.         elseif msp >=26 then
  108.             VanCapriceLevel=3
  109.         elseif msp >=24 then
  110.             VanCapriceLevel=2
  111.         else
  112.             VanCapriceLevel=1
  113.         end
  114.     end
  115. end
  116. function loadtimeouts()
  117.     if IsHomun(MyID)==1 then
  118.         dofile("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua")
  119.     else
  120.         dofile("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua")
  121.     end
  122.     if AggressiveRelogTracking==1 then
  123.         if IsHomun(MyID)==1 then
  124.             dofile(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua")
  125.         else
  126.             dofile(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua")
  127.         end
  128.     end
  129. end
  130.  
  131. --########################################
  132. --### Friend the merc/homun - old one  ###
  133. --### by Misch, new one by Dr. Azzy    ###
  134. --########################################
  135.  
  136. if (AssumeHomun==1) then
  137.     if (NewAutoFriend==0) then
  138.         ff = {}
  139.         Hactors = GetActors()
  140.         Howner  = GetV(V_OWNER,Hactors[1])
  141.         FX = 0
  142.         FY = 0
  143.         OX,OY = GetV(V_POSITION,Howner)
  144.         for i,v in ipairs(Hactors) do
  145.             if (v ~= Howner) then
  146.                 if (IsMonster(v)==0) then
  147.                     FX,FY = GetV(V_POSITION,v)
  148.                     if (FX==OX and FY==OY and v<100000) then --is not a player
  149.                         MyFriends[v]=2
  150.                     end
  151.                 end
  152.             end
  153.         end
  154.     else
  155.         NeedToDoAutoFriend=1
  156.         TraceAI("Setting NeedToDoAutoFriend")
  157.     end
  158. end
  159. --####################################
  160. --######## Command Processing ########
  161. --####################################
  162.  
  163. function    OnMOVE_CMD (x,y)
  164.     TraceAI ("OnMOVE_CMD")
  165.     if GetDistanceAPR(GetV(V_OWNER,MyID),x,y) > 15 or x==0 or y==0 then -- Bogus move command
  166.         local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  167.         logappend("AAI_ERROR","move command to invalid location "..formatpos(x,y).." owner pos "..formatpos(ox,oy))
  168.         TraceAI("OnMOVE_CMD - Command disregarded; invalid location logged")
  169.         return
  170.     end
  171.    
  172.     ResetCounters()
  173.     if ( x == MyDestX and y == MyDestY and MOTION_MOVE == GetV(V_MOTION,MyID)) then
  174.         return
  175.     end
  176.  
  177.     --local curX, curY = GetV (V_POSITION,MyID)
  178.     --if (math.abs(x-curX)+math.abs(y-curY) > 15) then
  179.     --  List.pushleft (ResCmdList,{MOVE_CMD,x,y})
  180.     --  x = math.floor((x+curX)/2)  
  181.     --  y = math.floor((y+curY)/2)
  182.     --end
  183.     MyMoveX,MyMoveY=x,y
  184.     MyDestX,MyDestY=x,y
  185.     Move (MyID,MyDestX,MyDestY)
  186.     MyState = MOVE_CMD_ST
  187.     if (MirAIFriending==1) then --emulate MirAI's annoying friending process
  188.         TraceAI("Starting Friend Routine")
  189.         local actors = GetActors()
  190.         for i,v in ipairs(actors) do
  191.             if (IsMonster(v)~=1 and v~=GetV(V_OWNER,MyID) and v~=MyID) then
  192.                 xx,yy=GetV(V_POSITION,v)
  193.                 if (xx==x and (yy+1==y or yy-1==y)) then
  194.                    
  195.                     if (MyFriends[v]==nil) then
  196.                         MyFriends[v] = 1
  197.                         UpdateFriends()
  198.                         MyState=FRIEND_CIRCLE_ST
  199.                         TraceAI("Friend Addition")
  200.                         NewFriendX,NewFriendY=GetV(V_POSITION,v)
  201.                         return
  202.                     elseif (MyFriends[v]~=nil) then
  203.                         MyFriends[v] = nil
  204.                         UpdateFriends()
  205.                         MyState=FRIEND_CROSS_ST
  206.                         TraceAI("Friend Removal")
  207.                         NewFriendX,NewFriendY=GetV(V_POSITION,v)
  208.                         return
  209.                     end
  210.                 end
  211.             end
  212.         end
  213.     end
  214. end
  215.  
  216.  
  217. function    OnSTOP_CMD ()
  218.  
  219.     TraceAI ("OnSTOP_CMD")
  220.     logappend("AAI_ERROR","STOP_CMD sent! This should NEVER HAPPEN!")
  221.     if (GetV(V_MOTION,MyID) ~= MOTION_STAND) then
  222.         Move (MyID,GetV(V_POSITION,MyID))
  223.     end
  224.     MyState = IDLE_ST
  225.     MyDestX = 0
  226.     MyDestY = 0
  227.     MyEnemy = 0
  228.     EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  229.     EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  230.     MySkill = 0
  231.  
  232. end
  233.  
  234.  
  235. function    OnATTACK_OBJECT_CMD (id)
  236.     TraceAI ("OnATTACK_OBJECT_CMD")
  237.     ResetCounters()
  238.     MySkill = 0
  239.     MyEnemy = id
  240.     BypassKSProtect=1
  241.     if (UseBerserkAttack==1) then
  242.         BerserkMode=1
  243.     end
  244.     MyState = CHASE_ST
  245.     OnCHASE_ST()
  246. end
  247.  
  248.  
  249. function    OnATTACK_AREA_CMD (x,y)
  250.  
  251.     TraceAI ("OnATTACK_AREA_CMD")
  252.     logappend("AAI_ERROR","ATTACK_AREA_CMD sent! This should NEVER HAPPEN!")
  253.     if (x ~= MyDestX or y ~= MyDestY or MOTION_MOVE ~= GetV(V_MOTION,MyID)) then
  254.         Move (MyID,x,y)
  255.     end
  256.     MyDestX = x
  257.     MyDestY = y
  258.     MyEnemy = 0
  259.     EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  260.     EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  261.     MyState = ATTACK_AREA_CMD_ST
  262.    
  263. end
  264.  
  265. function    OnPATROL_CMD (x,y)
  266.  
  267.     TraceAI ("OnPATROL_CMD")
  268.     logappend("AAI_ERROR","PATROL_CMD sent! This should NEVER HAPPEN!")
  269.     MyPatrolX , MyPatrolY = GetV (V_POSITION,MyID)
  270.     MyDestX = x
  271.     MyDestY = y
  272.     Move (MyID,x,y)
  273.     MyState = PATROL_CMD_ST
  274.  
  275. end
  276.  
  277.  
  278. function    OnHOLD_CMD ()
  279.  
  280.     TraceAI ("OnHOLD_CMD")
  281.     logappend("AAI_ERROR","HOLD_CMD sent! This should NEVER HAPPEN!")
  282.     MyDestX = 0
  283.     MyDestY = 0
  284.     MyEnemy = 0
  285.     EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  286.     EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  287.     MyState = HOLD_CMD_ST
  288.  
  289. end
  290.  
  291.  
  292. function    OnSKILL_OBJECT_CMD (level,skill,id)
  293.  
  294.     TraceAI ("OnSKILL_OBJECT_CMD"..skill.." "..id.." "..level)
  295.     ResetCounters()
  296.     if skill==MH_PAIN_KILLER and IsPlayer(id)==1 and PainkillerFriends~=0 and id~=GetV(V_OWNER,MyID) then
  297.         MyPState = MyState
  298.         MyState = PROVOKE_ST
  299.         MyPEnemy = id
  300.         MyPSkill = skill
  301.         MyPSkillLevel = level
  302.         MyPMode = id
  303.         if MyFriends[id]~=FRIEND then
  304.             MyFriends[id]=PKFRIEND
  305.             if PainkillerFriendsSave==1 then
  306.                 UpdateFriends()
  307.             end
  308.         end
  309.         return OnPROVOKE_ST()
  310.     else
  311.         MySkillLevel = level
  312.         MySkill = skill
  313.         MyEnemy = id
  314.         if IsMonster(id)==1 and SuperPassive~=1 then
  315.             BypassKSProtect=1
  316.             if (UseBerserkSkill==1) then
  317.                 BerserkMode=1
  318.             end
  319.             MyState = CHASE_ST
  320.             OnCHASE_ST()
  321.         else
  322.             MyState = SKILL_OBJECT_CMD_ST
  323.         end
  324.     end
  325. end
  326.  
  327.  
  328. function    OnSKILL_AREA_CMD (level,skill,x,y)
  329.     ResetCounters()
  330.     TraceAI ("OnSKILL_AREA_CMD")
  331.     targetx,targety=Closest(MyID,x,y,AttackRange(MyID,skill,level))
  332.     Move (MyID,targetx,targety)
  333.     MyDestX = x
  334.     MyDestY = y
  335.     MySkillLevel = level
  336.     MySkill = skill
  337.     MyState = SKILL_AREA_CMD_ST
  338.    
  339. end
  340.  
  341. function    OnFOLLOW_CMD ()
  342.     ReturnToMoveHold = 0
  343.     if (MyState ~= FOLLOW_CMD_ST) then
  344.         if StickyStandby > 0 then
  345.             ShouldStandby=1
  346.         end
  347.         BetterMoveToOwner (MyID,FollowStayBack)
  348.         MyState = FOLLOW_CMD_ST
  349.         MyEnemy = 0
  350.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  351.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  352.         MySkill = 0
  353.         TraceAI ("OnFOLLOW_CMD")
  354.     else
  355.         if StickyStandby > 0 then
  356.             ShouldStandby=0
  357.         end
  358.         MyState = IDLE_ST
  359.         MyEnemy = 0
  360.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  361.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  362.         MySkill = 0
  363.         TraceAI ("FOLLOW_CMD_ST --> IDLE_ST")
  364.     end
  365.  
  366. end
  367.  
  368.  
  369.  
  370.  
  371. function    ProcessCommand (msg)
  372.  
  373.     if  (msg[1] == MOVE_CMD) then
  374.         TraceAI ("MOVE_CMD")
  375.         OnMOVE_CMD (msg[2],msg[3])
  376.     elseif  (msg[1] == STOP_CMD) then
  377.         TraceAI ("STOP_CMD")
  378.         OnSTOP_CMD ()
  379.     elseif  (msg[1] == ATTACK_OBJECT_CMD) then
  380.         TraceAI ("ATTACK_OBJECT_CMD")
  381.         OnATTACK_OBJECT_CMD (msg[2])
  382.     elseif  (msg[1] == ATTACK_AREA_CMD) then
  383.         TraceAI ("ATTACK_AREA_CMD")
  384.         OnATTACK_AREA_CMD (msg[2],msg[3])
  385.     elseif  (msg[1] == PATROL_CMD) then
  386.         TraceAI ("PATROL_CMD")
  387.         OnPATROL_CMD (msg[2],msg[3])
  388.     elseif  (msg[1] == HOLD_CMD) then
  389.         TraceAI ("HOLD_CMD")
  390.         OnHOLD_CMD ()
  391.     elseif  (msg[1] == SKILL_OBJECT_CMD) then
  392.         TraceAI ("SKILL_OBJECT_CMD")
  393.         OnSKILL_OBJECT_CMD (msg[2],msg[3],msg[4],msg[5])
  394.     elseif  (msg[1] == SKILL_AREA_CMD) then
  395.         TraceAI ("SKILL_AREA_CMD")
  396.         OnSKILL_AREA_CMD (msg[2],msg[3],msg[4],msg[5])
  397.     elseif  (msg[1] == FOLLOW_CMD) then
  398.         TraceAI ("FOLLOW_CMD")
  399.         OnFOLLOW_CMD ()
  400.     end
  401. end
  402.  
  403. function ResetCounters()
  404.     MyPState                = 0
  405.     MyPSkill                = 0
  406.     MyPEnemy                = 0
  407.     MyPSkillLevel           = 0
  408.     MySkillUsedCount        = 0
  409.     ChaseGiveUpCount        = 0
  410.     AttackGiveUpCount       = 0
  411.     ChaseDebuffUsed         = 0
  412.     AttackDebuffUsed        = 0
  413.     BypassKSProtect         = 0
  414.     BerserkMode             = 0
  415.     ReturnToState           = 0
  416.     NewFriend               = 0
  417.     FriendMotionTime        = 0
  418.     FriendCircleIter        = 0
  419.     FriendCircleTimeout     = 0
  420.     AtkPosbugFixTimeout     = 0
  421.     SkillObjectCMDTimeout   = 0
  422.     FollowTryCount          = 0
  423.     MyMoveX,MyMoveY         = 0,0
  424.     if CastSkillMode < 0 then
  425.         CastSkill=0
  426.         CastSkillLevel=0
  427.         CastSkillMode=0
  428.     end
  429.     return
  430. end
  431.  
  432. --###############################
  433. --######## State Process ########
  434. --###############################
  435.  
  436.  
  437. function    OnIDLE_ST ()
  438.     --if ReturnToMoveHold~=0 then
  439.     --  MyState=MOVE_CMD_HOLD_ST
  440.     --  OnMOVE_CMD_HOLD_ST()
  441.     --  return
  442.     --end
  443.     TraceAI ("OnIDLE_ST")
  444.     ResetCounters()
  445.     MySkill                 = 0
  446.     MyDestX                 = 0
  447.     MyDestY                 = 0
  448.     MyEnemy                 = 0
  449.     if (DoIdleTasks()==nil) then
  450.         return
  451.     end
  452.     --StickyStandby handling
  453.     if (ShouldStandby==1 and StickyStandby > 0) then
  454.         MyState=FOLLOW_CMD_ST
  455.         return
  456.     end
  457.     --Targeting
  458.     if SuperPassive~=1 then
  459.         local   object = SelectEnemy(GetFriendTargets())
  460.         if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN
  461.             MyState = CHASE_ST
  462.             MyEnemy = object
  463.             TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
  464.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  465.                 OnCHASE_ST()
  466.             end
  467.             return
  468.         end
  469.         if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0) ) then
  470.             aggro=1
  471.         else
  472.             aggro=0
  473.         end
  474.         object=SelectEnemy(GetEnemyList(MyID,aggro))
  475.         if object~=0 then
  476.             MyState = CHASE_ST
  477.             MyEnemy = object
  478.             TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN")
  479.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  480.                 return OnCHASE_ST()
  481.             end
  482.             return 
  483.         end
  484.         if (aggro==1 and TankMonsterCount < TankMonsterLimit) then
  485.             object = SelectEnemy(GetEnemyList(MyID,-1))
  486.             if (object ~= 0) then
  487.                 MyState = TANKCHASE_ST
  488.                 MyEnemy = object
  489.                 TraceAI ("IDLE_ST -> TANKCHASE_ST")
  490.                 return
  491.             end
  492.         end
  493.     end
  494.     --Following
  495.     local distance
  496.     if ReturnToMoveHold ~=0 then
  497.         distance = GetDistanceAP(MyID,StickyX,StickyY)
  498.     else
  499.         distance = GetDistanceFromOwner(MyID)
  500.     end
  501.     if (UseIdleWalk~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP)) then -- CHECK
  502.         if ( distance > GetMoveBounds() or distance == -1) then     -- MYOWNER_OUTSIGNT_IN
  503.             MyState = FOLLOW_ST
  504.             TraceAI ("IDLE_ST -> FOLLOW_ST")
  505.             return
  506.         else
  507.             TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk)
  508.             MyState=IDLEWALK_ST
  509.         end
  510.     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
  511.         MyState = FOLLOW_ST
  512.         TraceAI ("IDLE_ST -> FOLLOW_ST")
  513.         return OnFOLLOW_ST()
  514.     end
  515.     if UseAutoHeal==3 then
  516.         if DoHealingTasks(MyID) == 1 then
  517.             return
  518.         end
  519.     end
  520.     DoAutoBuffs(-2)
  521.     if UseIdleWalk ~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP) then
  522.         TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk)
  523.         MyState=IDLEWALK_ST
  524.     end
  525. end
  526.  
  527. function    OnFOLLOW_ST ()
  528.  
  529.     TraceAI ("OnFOLLOW_ST - follow try count: "..FollowTryCount.." ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10))
  530.     local dist = GetDistanceFromOwner(MyID)
  531.     if dist > GetMoveBounds() then
  532.         ReturnToMoveHold = 0
  533.         StickyX,StickyY=0,0
  534.     end
  535.     if ReturnToMoveHold ~=0 then
  536.         dist = GetDistanceAP(MyID,StickyX,StickyY)
  537.     end
  538.     if (dist <= DiagonalDist(FollowStayBack+1)) then        --  DESTINATION_ARRIVED_IN
  539.         FollowTryCount=0
  540.         MyState = IDLE_ST
  541.         TraceAI ("FOLLOW_ST -> IDLE_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10))
  542.         if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
  543.             FastChangeCount = FastChangeCount+1
  544.             return OnIDLE_ST()
  545.         end
  546.     else
  547.         if (FollowTryCount > FollowTryPanic and GetV(V_MOTION,MyID)~=MOTION_MOVE) then
  548.             if FollowTryCount > 2*FollowTryPanic then
  549.                 if FollowTryCount > 3*FollowTryPanic then
  550.                     TraceAI("FOLLOW_ST -> IDLE_ST - Can't follow even using panic'ed methods - Giving up")
  551.                     MyState=IDLE_ST
  552.                     if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
  553.                         FastChangeCount = FastChangeCount+1
  554.                         return OnIDLE_ST()
  555.                     end
  556.                 else
  557.                     if ReturnToMoveHold ==0 then
  558.                         MoveToOwner(MyID)
  559.                         FollowTryCount=FollowTryCount+1
  560.                         TraceAI("Emergency follow - MoveToOwner() called")
  561.                     else
  562.                         TraceAI("FOLLOW_ST -> IDLE_ST - Can't get to move sticky location even using panic'ed methods - Giving up")
  563.                         MyState=IDLE_ST
  564.                         if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then
  565.                             FastChangeCount = FastChangeCount+1
  566.                             return OnIDLE_ST()
  567.                         end
  568.                     end
  569.                 end
  570.             else
  571.                 FollowTryCount=FollowTryCount+1
  572.                 if ReturnToMoveHold==0 then
  573.                     BetterMoveToOwner (MyID,1)
  574.                 else
  575.                     BetterMoveToLoc(MyID,1,StickyX,StickyY)
  576.                 end
  577.             end
  578.         else
  579.             local dx,dy
  580.             if ReturnToMoveHold==0 then
  581.                 dx,dy=BetterMoveToOwnerXY(MyID,FollowStayBack)
  582.             else
  583.                 dx,dy=BetterMoveToLocXY(MyID,FollowStayBack,StickyX,StickyY)
  584.             end
  585.             local mx,my=GetV(V_POSITION,MyID)
  586.             --TraceAI("followobstacle dest"..formatpos(dx,dy).." pos "..formatpos(mx,my))
  587.             if math.abs(my-dy) <=1 then
  588.                 --TraceAI("math.abs(my-dy) <=1")
  589.                 if math.abs(mx-dx) <=1 then --We could be in a bounce loop, better see if we are
  590.                     --TraceAI("math.abs(my-dy) <=1")
  591.                     if MyPosY[1]==my then
  592.                         for v=2,5,1 do
  593.                             if MyPosX[v]==dx then
  594.                                 MoveToOwner(MyID)
  595.                                 FollowTryCount=FollowTryCount+1
  596.                                 TraceAI("Bounce loop detected, emergency measures taken")
  597.                                 break
  598.                             end
  599.                             if v==5 then
  600.                                 MyDestX,MyDestY=dx,dy
  601.                                 Move(MyID,MyDestX,MyDestY)
  602.                             end
  603.                         end
  604.                     else
  605.                         MyDestX,MyDestY=dx,dy
  606.                         Move(MyID,MyDestX,MyDestY)
  607.                     end
  608.                 else
  609.                     --TraceAI("math.abs(mx-dx) > 1"..math.abs(mx-dx))
  610.                     MyDestX,MyDestY=dx,dy
  611.                     Move(MyID,MyDestX,MyDestY)
  612.                 end
  613.             else
  614.                 --TraceAI("math.abs(my-dy) > 1"..math.abs(my-dy))
  615.                 MyDestX,MyDestY=dx,dy
  616.                 Move(MyID,MyDestX,MyDestY)
  617.             end
  618.             if (GetV(V_MOTION,MyID) ~= MOTION_MOVE) then
  619.                 FollowTryCount=FollowTryCount+1
  620.             else
  621.                 FollowTryCount=0
  622.             end
  623.         end
  624.         TraceAI ("FOLLOW_ST -> FOLLOW_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10).."dest cell"..formatpos(MyDestX,MyDestY))
  625.         return
  626.     end
  627. end
  628.  
  629.  
  630. function    OnCHASE_ST ()
  631.     MyAttackStanceX,MyAttackStanceY = 0,0
  632.     TraceAI ("OnCHASE_ST")
  633.     aggro = GetAggroCount()
  634.     if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then
  635.         BerserkMode=1
  636.     end
  637.     if DoAutoBuffs(-1) == 1 then
  638.         DoAutoBuffs(2)
  639.     end
  640.     if (UseSkillOnly==1 and MySkill ~= 0) then
  641.         skill,level=GetAtkSkill(MyID)
  642.     else
  643.         skill=nil
  644.         level=nil
  645.     end
  646.     if true==IsOutOfSight(MyID,MyEnemy) then
  647.         value="true "
  648.     else
  649.         value="false"
  650.     end
  651.     if(IsNotKS(MyID,MyEnemy)==0) then
  652.         local reason=GetKSReason(MyID,MyEnemy)
  653.         TraceAI ("CHASE_ST -> IDLE_ST : Enemy is taken "..reason)
  654.         MyState = IDLE_ST
  655.         MyEnemy = 0
  656.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  657.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  658.         MyDestX, MyDestY = 0,0
  659.         ChaseGiveUpCount=0
  660.         if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  661.             FastChangeCount = FastChangeCount+1
  662.             return OnIDLE_ST()
  663.         end
  664.     end
  665.     if (true == IsOutOfSight(MyID,MyEnemy)) then    -- ENEMY_OUTSIGHT_IN
  666.    
  667.         MyState = IDLE_ST
  668.         MyEnemy = 0
  669.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  670.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  671.         MyDestX, MyDestY = 0,0
  672.         TraceAI ("CHASE_ST -> IDLE_ST : Enemy out of sight")
  673.         ChaseGiveUpCount=0
  674.        
  675.         if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  676.             FastChangeCount = FastChangeCount+1
  677.             return OnIDLE_ST()
  678.         else
  679.             return
  680.         end
  681.     end
  682.     if GetV(V_MOTION,MyID)~=MOTION_MOVE then
  683.         if ChaseGiveUpCount > ChaseGiveUp then
  684.             Unreachable[MyEnemy]=1
  685.             if SelectEnemy(GetEnemyList(MyID,-2)) == MyEnemy then --Oh crap,
  686.                 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")
  687.                 MyState = FOLLOW_ST
  688.                 MyEnemy = 0
  689.                 EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  690.                 EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  691.                 MyDestX,MyDestY=0,0
  692.                 ChaseGiveUpCount=0
  693.                 return OnFOLLOW_ST()
  694.             else
  695.                 MyState = IDLE_ST
  696.                 MyDestX, MyDestY = 0,0
  697.                 TraceAI ("CHASE_ST -> IDLE_ST : Marking target "..MyEnemy.." unreachable")
  698.                 ChaseGiveUpCount=0
  699.                 MyEnemy = 0
  700.                 EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  701.                 EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  702.                 if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  703.                     FastChangeCount = FastChangeCount+1
  704.                     return OnIDLE_ST()
  705.                 else
  706.                     return
  707.                 end
  708.             end
  709.         end
  710.         ChaseGiveUpCount=ChaseGiveUpCount+1
  711.     elseif MyEnemies[3]==MyEnemy and GetDistanceAPR(MyEnemy,MyPosX[3],MyPosY[3]) <= GetDistanceAR(MyID,MyEnemy) then
  712.         ChaseGiveUpCount=ChaseGiveUpCount+1
  713.         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")
  714.     end
  715.     OnChaseStart()
  716.     if OpportunisticTargeting ==1 and MySkill==0 and SuperPassive~=1 then
  717.         if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then
  718.             aggro=1
  719.         else
  720.             aggro=0
  721.         end
  722.         object=SelectEnemy(GetEnemyList(MyID,aggro),MyEnemy)
  723.         if object ~= 0 then
  724.             TraceAI("Opportunistic target change - dropping target "..MyEnemy.." for target "..object)
  725.             MyEnemy=object 
  726.             EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  727.             EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  728.         end
  729.     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
  730.         if EleanorDoNotSwitchMode == 1 then
  731.             TraceAI("Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1")
  732.             logappend("AAI_ERROR","Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1")
  733.         else
  734.             if AutoSkillTimeout < GetTick() then
  735.                 DoSkill(MH_STYLE_CHANGE,1,MyID,8)
  736.             end
  737.         end
  738.     end
  739.     if (true == IsInAttackSight(MyID,MyEnemy,skill,level)) then  -- ENEMY_INATTACKSIGHT_IN
  740.         MyState = ATTACK_ST
  741.         AttackTimeout=GetTick()+AttackTimeLimit
  742.         ExChaseGiveUpCount=ChaseGiveUpCount
  743.         ChaseGiveUpCount=0
  744.         MySkillUsedCount=0
  745.         TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN")
  746.         if (FastChangeCount < FastChangeLimit and FastChange_C2A == 1) then
  747.             FastChangeCount = FastChangeCount+1
  748.             return OnATTACK_ST()
  749.         else
  750.             return
  751.         end
  752.     elseif UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then
  753.         dist=GetDistanceA(MyID,MyEnemy)
  754.         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)
  755.         skilltouse={-1,0,0}
  756.         local SkillList=GetTargetedSkills(MyID)
  757.         local availsp = GetV(V_SP,MyID)
  758.         if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then
  759.             availsp = availsp - tact_sp
  760.         end
  761.         TraceAI("Begin autoskill while chasing routine")
  762.         if (tact_skill < 0) then        -- Negative value of TACT_SKILL -> 1 cast of skill
  763.             skill_level=tact_skill*-1   -- with level = to the absolute value of the
  764.             tact_skill=1            -- value of TACT_SKILL.
  765.         else
  766.             skill_level=11
  767.         end
  768.         for i,v in ipairs(SkillList) do
  769.  
  770.             skilltype=v[1]
  771.             if v[2]~=0 then
  772.                 if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then
  773.                     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
  774.                         local mobskill_level=skill_level
  775.                         if AOEFixedLevel == 1 then
  776.                             mobskill_level=v[3]
  777.                         end
  778.                         local mobmode=0
  779.                         if AutoMobMode==2 then
  780.                             mobmode=1
  781.                         end
  782.                         mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode)
  783.                         --TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level)))
  784.                         if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then
  785.                             if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then
  786.                                 if (skilltouse[1] < 2) then
  787.                                     skilltouse=v
  788.                                 end
  789.                             end
  790.                         end
  791.                     elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then
  792.                             if (tact_debuff*-1 == v[2] or (tact_debuff==-1 and BasicDebuffs[v[2]]~=nil)) then
  793.                                 if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  794.                                     skilltouse=v
  795.                                 end
  796.                             end
  797.                     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
  798.                         if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  799.                             skilltouse=v
  800.                         end
  801.                     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
  802.                         if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  803.                             skilltouse=v
  804.                         end
  805.                     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
  806.                         if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  807.                             skilltouse=v
  808.                         end
  809.                     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
  810.                         if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  811.                             skilltouse=v
  812.                         end
  813.                     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
  814.                         if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  815.                             skilltouse=v
  816.                         end
  817.                     end
  818.                 end
  819.             end
  820.         end
  821.         if skilltouse[2]~=0 then
  822.             TraceAI("Using skill while chasing:"..skilltouse[2])
  823.             local slvl=skilltouse[3]
  824.             if skill_level~=11 then
  825.                 slvl=skill_level
  826.             end
  827.             DoSkill(skilltouse[2],slvl,MyEnemy)
  828.             if skilltouse[1] == DEBUFF_ATK then
  829.                 ChaseDebuffUsed=1
  830.             end
  831.             MySkillUsedCount=1
  832.         end
  833.     else
  834.         TraceAI("Not in range, and can't use chase skill")
  835.     end
  836.     if (GetTact(TACT_CHASE,MyEnemy)~=1) then
  837.         local alt = 0
  838.         if (ChaseGiveUpCount >= 4 and MyPosX[1] == MyPosX[3]  and MyPosY[1]==MyPosY[3]) then
  839.             alt=math.random(2)
  840.             TraceAI("Using alt movement"..alt)
  841.         end
  842.         ex,ey=GetV(V_POSITION,MyEnemy)
  843.         TraceAI("State History: "..MyStates[1].." "..MyStates[2].." "..MyStates[3].." "..MyStates[4].." "..MyStates[5])
  844.         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]))
  845.         TraceAI("Enemy History: "..MyEnemies[1].." "..MyEnemies[2].." "..MyEnemies[3].." "..MyEnemies[4].." "..MyEnemies[5])
  846.         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]))
  847.         TraceAI("current enemy: "..MyEnemy.." "..formatpos(ex,ey))
  848.         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
  849.             x,y=AdjustOpp(x,y,ex,ey)
  850.             Unreachable[MyEnemy]=1
  851.             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.")
  852.         elseif EnemyPosX[3]~=0 and EnemyPosY[3]~=0 and (ex~=EnemyPosX[3] or ey~=EnemyPosY[3]) and MyEnemy==MyEnemies[3] and alt==0 then
  853.             dx,dy = ex-EnemyPosX[3],ey-EnemyPosY[3]
  854.             r=AttackRange(MyID,MySkill,MySkillLevel)
  855.             x,y = Closest(MyID,ex+dx,ey+dy,r,alt)
  856.             x,y = AdjustStandPoint(x,y,ex,ey,r,alt)
  857.         else
  858.             x,y = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
  859.             if x==-1 or y==-1 then
  860.                 if AttackRange(MyID,MySkill,MySkillLevel) < 2 or alt > 0 then
  861.                     MyState = IDLE_ST
  862.                     Unreachable[MyEnemy]=1
  863.                     MyEnemy = 0
  864.                     EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  865.                     EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  866.                     MyDestX, MyDestY = 0,0
  867.                     TraceAI ("CHASE_ST -> IDLE_ST : Cannot attack this target, GetStandPoint() reports that all cells around it are occupied.")
  868.                     ChaseGiveUpCount=0
  869.                     if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  870.                         FastChangeCount = FastChangeCount+1
  871.                         return OnIDLE_ST()
  872.                     end
  873.                 else
  874.                     x,y = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,1)
  875.                     if x==-1 or y==-1 then
  876.                         MyState = IDLE_ST
  877.                         Unreachable[MyEnemy]=1
  878.                         MyEnemy = 0
  879.                         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  880.                         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  881.                         MyDestX, MyDestY = 0,0
  882.                         TraceAI ("CHASE_ST -> IDLE_ST : Cannot attack this target, GetStandPoint() can't get an unoccupied cell")
  883.                         ChaseGiveUpCount=0
  884.                         if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  885.                             FastChangeCount = FastChangeCount+1
  886.                             return OnIDLE_ST()
  887.                         end
  888.                     end
  889.                 end
  890.             end
  891.         end
  892.         ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  893.         if GetDistanceAPR(GetV(V_OWNER,MyID),x,y) < GetMoveBounds() then
  894.             if ((x~=MyDestX or y~=MyDestY) or GetV(V_MOTION,MyID)~=MOTION_MOVE)  then
  895.                 MyDestX, MyDestY=x,y
  896.                 Move (MyID,MyDestX,MyDestY)
  897.                 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))
  898.             else
  899.                 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))
  900.             end
  901.         else --if ChaseGiveUpCount > 4 then
  902.             MyState = IDLE_ST
  903.             Unreachable[MyEnemy]=1
  904.             MyEnemy = 0
  905.             EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  906.             EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  907.             MyDestX, MyDestY = 0,0
  908.             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))
  909.             ChaseGiveUpCount=0
  910.             if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then
  911.                 FastChangeCount = FastChangeCount+1
  912.                
  913.                 return OnIDLE_ST()
  914.             end
  915.         end
  916.     end
  917.     return
  918. end
  919.  
  920.  
  921.  
  922.  
  923. function OnATTACK_ST ()
  924.     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))  
  925.     if (true == IsOutOfSight(MyID,MyEnemy)) then -- first thing's first, if enemy is gone drop it.
  926.         MyState = IDLE_ST
  927.         MyEnemy = 0
  928.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  929.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  930.         MySkillUseCount= 0
  931.         TraceAI ("ATTACK_ST -> IDLE_ST -- target gone")
  932.         return OnIDLE_ST()
  933.     end
  934.     if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then   -- Enemy dead? Okay we're done here - drop it.
  935.         MyState = IDLE_ST
  936.         MyEnemy = 0
  937.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  938.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  939.         MySkillUseCount= 0
  940.         TraceAI ("ATTACK_ST -> IDLE_ST  Enemy dead")
  941.         return OnIDLE_ST()
  942.     end
  943.     local mytarg=GetV(V_TARGET,MyID)
  944.     if mytarg~=MyEnemy and MyStates[1]==ATTACK_ST then
  945.         AttackGiveUpCount=AttackGiveUpCount+1
  946.         if AttackGiveUpCount > 2 then --MyEnemies[3]==MyEnemy and MyStates[3]==ATTACK_ST and MyStates[2]==ATTACK_ST then
  947.             local tx,ty=GetV(V_POSITION,MyEnemy)
  948.             local x,y=GetV(V_POSITION,MyID)
  949.             if AttackGiveUpCount < 5 then
  950.                 Move(MyID,tx,ty)
  951.                 TraceAI("ATTACK_ST: We've been attacking for 3 cycles, but we still haven't attacked! Something is wrong - Moving to monster cell")
  952.             elseif AttackGiveUpCount < AttackGiveUp then
  953.                 nx,ny=AdjustOpp(x,y,tx,ty)
  954.                 Move(MyID,tx,ty)
  955.                 TraceAI("ATTACK_ST: We've been attacking for 3 cycles, but we still haven't attacked! Something is wrong - Moving to adjust opposite")
  956.             elseif AttackGiveUpCount > AttackGiveUp and MyEnemies[AttackGiveUp]==MyEnemy and MyStates[AttackGiveUp]==ATTACK_ST then
  957.                 MyState = IDLE_ST
  958.                 Unreachable[MyEnemy]=1
  959.                 MyEnemy = 0
  960.                 EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  961.                 EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  962.                 MySkillUseCount= 0
  963.                 TraceAI("ATTACK_ST -> IDLE_ST - We've been attacking for 5 cycles, tried moving around, and still haven't attacked it. Marking unreachable")
  964.                 return OnIDLE_ST()
  965.             end
  966.         end
  967.     else --We have attacked it successfully
  968.         if GetV(V_MOTION,MyEnemy)==MOTION_DAMAGE then
  969.             local t=1
  970.             for i,v in ipairs(GetActors()) do
  971.                 if v~=MyID then
  972.                     if GetV(V_TARGET,v)==MyEnemy then
  973.                         t=0
  974.                         break
  975.                     end
  976.                 end
  977.             end
  978.             if t==1 then
  979.                 AttackTimeout=GetTick()+AttackTimeLimit
  980.                 TraceAI("AttackTimeout Reset - we're clearly attacking successfully")
  981.             end
  982.         end
  983.     end
  984.     if (AttackTimeout < GetTick() and AttackTimeLimit > 0) then -- Attack time limit reached.
  985.         MyState = FOLLOW_ST
  986.         Unreachable[MyEnemy]=1
  987.         MyEnemy = 0
  988.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  989.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  990.         MySkillUseCount= 0
  991.         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")
  992.         return OnFOLLOW_ST()
  993.     end
  994.    
  995.     local aggro = GetAggroCount()
  996.     if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then
  997.         BerserkMode=1
  998.     end
  999.     DoAutoBuffs(2)
  1000.     local skill,level
  1001.     if UseSkillOnly==1 then
  1002.         skill,level=GetAtkSkill(MyID)
  1003.     elseif MySkill~=0 then
  1004.         skill,level=MySkill,MySkillLevel
  1005.     else
  1006.         skill,level=nil,nil
  1007.     end
  1008.     if (false == IsInAttackSight(MyID,MyEnemy,skill,level)) then  -- Check if we can attack enemy, if not back to chase
  1009.         ResetCounters()
  1010.         MyState=CHASE_ST
  1011.         TraceAI ("ATTACK_ST -> CHASE_ST  : ENEMY_OUTATTACKSIGHT_IN MyEnemy: "..MyEnemy.." distance to "..GetDistanceA(MyID,MyEnemy))
  1012.         if (FastChangeCount < FastChangeLimit and FastChange_A2C == 1) then
  1013.             FastChangeCount = FastChangeCount+1
  1014.             return OnCHASE_ST()
  1015.         end
  1016.     end
  1017.     if (MyAttackStanceX==0) then
  1018.         x,y=GetV(V_POSITION,MyID)
  1019.         MyAttackStanceX,MyAttackStanceY=x,y
  1020.         logappend("AAI_DANCE","Attack Stance set to "..MyDestX..","..MyDestY.." current pos: "..x..","..y)
  1021.     end
  1022.     if (UseAutoPushback > 0) then
  1023.         if DoAutoPushback(MyID)  == nil then
  1024.             return
  1025.         end
  1026.     end
  1027.     OnAttackStart()
  1028.     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)
  1029.     local skill_level
  1030.    
  1031.     --logappend("AAI_ATK","tact_skill set "..tact_skill)
  1032.     --if AutoMobMode==1 then
  1033.     --  mskill,mlevel=GetMobSkill(MyID)
  1034.     --  mobcount=GetMobCount(mskill,mlevel,MyEnemy,1)
  1035.     --  --TraceAI("mskill/level "..FormatSkill(mskill,mlevel).." mobcount "..mobcount)
  1036.     --elseif AutoMobMode==2 then
  1037.     --  mskill,mlevel=GetMobSkill(MyID)
  1038.     --  mobcount=GetMobCount(mskill,mlevel,MyEnemy,0)
  1039.     --else
  1040.     --  mobcount=0
  1041.     --end
  1042.     --Sniping routine
  1043.     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
  1044.         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.
  1045.         if target ~=0 then
  1046.             snipeskill=0
  1047.             local snipe_tact_skillclass=GetTact(TACT_SKILLCLASS,target)
  1048.             if snipe_tact_skillclass == CLASS_MINION then
  1049.                 snipeskill,snipelevel=GetMinionSkill(MyID)
  1050.             end
  1051.             if snipeskill==0 and (snipe_tact_skillclass == CLASS_S or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_S) then
  1052.                 snipeskill,snipelevel=GetSAtkSkill(MyID)
  1053.             end
  1054.             if snipeskill==0 and (snipe_tact_skillclass == CLASS_OLD or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_OLD) then
  1055.                 snipeskill,snipelevel=GetAtkSkill(MyID)
  1056.             end
  1057.             --TraceAI("snipe "..skill.." level"..level)
  1058.             if snipeskill ~=0 then
  1059.                 slevel = GetTact(TACT_SKILL,target)
  1060.                 if slevel < 0 then
  1061.                     slevel=-1*slevel
  1062.                     if slevel > snipelevel then
  1063.                         slevel = snipelevel
  1064.                     end
  1065.                     if ((GetV(V_SP,MyID)-ReserveSP >= GetTact(TACT_SP,target)+GetSkillInfo(snipeskill,3,slevel))) then
  1066.                         TraceAI("Snipe attack on "..target.." "..snipeskill.." "..slevel)
  1067.                         DoSkill(snipeskill,slevel,target)
  1068.                     end
  1069.                 end
  1070.             end
  1071.         end
  1072.     end
  1073.    
  1074.    
  1075.     -- Begin skill selection routine
  1076.     skilltouse = {-1,0,0}
  1077.     -- First digit (1): -1 = no skill, 0 single target, 1 debuff, 2 mob
  1078.     -- Second digit (2): skill id
  1079.     -- Third digit (3): skill level
  1080.    
  1081.     if (1==1) then --non paniced attack
  1082.         if (MySkill==0 and UseAttackSkill == 1 and GetTick() >= AutoSkillTimeout) then 
  1083.             if (tact_skill < 0) then        -- Negative value of TACT_SKILL -> 1 cast of skill
  1084.                 skill_level=tact_skill*-1   -- with level = to the absolute value of the
  1085.                 tact_skill=1            -- value of TACT_SKILL.
  1086.             else
  1087.                 skill_level=11
  1088.             end
  1089.             local SkillList=GetTargetedSkills(MyID)
  1090.             TraceAI("Begin autoskill routine")
  1091.             local availsp = GetV(V_SP,MyID)
  1092.             if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then
  1093.                 availsp = availsp - tact_sp
  1094.             end
  1095.             for i,v in ipairs(SkillList) do
  1096.                 skilltype=v[1]
  1097.                 TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass"..tact_skillclass.."v"..v[1].." "..v[2].." "..v[3])       
  1098.                 if v[2]~=0 then
  1099.                     if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then
  1100.                         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
  1101.                             local mobskill_level=skill_level
  1102.                             if AOEFixedLevel == 1 then
  1103.                                 mobskill_level=v[3]
  1104.                             end
  1105.                             local mobmode=0
  1106.                             if AutoMobMode==2 then
  1107.                                 mobmode=1
  1108.                             end
  1109.                             mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode)
  1110.                             --TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level)))
  1111.                             if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then
  1112.                                 if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then
  1113.                                     if (skilltouse[1] < 2) then
  1114.                                         skilltouse=v
  1115.                                     end
  1116.                                 end
  1117.                             end
  1118.                         elseif (skilltype ==DEBUFF_ATK and AttackDebuffUsed < AttackDebuffLimit and (IsFriendOrSelf(GetV(V_TARGET,MyEnemy))==1 or AttackDebuffWhenAttacked~=1)) then
  1119.                             if (tact_debuff == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then
  1120.                                 if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1121.                                     skilltouse=v
  1122.                                 end
  1123.                             end
  1124.                         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
  1125.                             if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1126.                                 skilltouse=v
  1127.                             end
  1128.                         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
  1129.                             if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1130.                                 skilltouse=v
  1131.                             end
  1132.                         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
  1133.                             if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1134.                                 skilltouse=v
  1135.                             end
  1136.                         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
  1137.                             if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1138.                                 skilltouse=v
  1139.                             end
  1140.                         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
  1141.                             if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then
  1142.                                 skilltouse=v
  1143.                             end
  1144.                         end
  1145.                     end
  1146.                 end
  1147.                 TraceAI("skill selected "..skilltouse[2])
  1148.             end
  1149.         end
  1150.         -- Now we finalize the selection
  1151.         if skilltouse[1]~= -1 then
  1152.             MySkill=skilltouse[2]
  1153.             if (IsHomun(MyID)==1 and skill_level~=11 and (skilltouse[1]~=MOB_ATK or AOEFixedLevel ~= 1)) then   --no need to check what skill
  1154.                 MySkillLevel=skill_level            --Only homuns can use non-max level
  1155.             else                            --and they dont have any mob/debuffs
  1156.                 MySkillLevel=skilltouse[3]
  1157.             end
  1158.             if (skilltouse[1] == DEBUFF_ATK) then
  1159.                 AttackDebuffUsed=AttackDebuffUsed+1
  1160.             else
  1161.                 MySkillUsedCount=MySkillUsedCount+1
  1162.             end
  1163.         end
  1164.     end
  1165.    
  1166.     -- Now we resolve it
  1167.    
  1168.     --if (MySkill == 0) then
  1169.     if (UseSkillOnly ~= 1) then
  1170.         Attack (MyID,MyEnemy)
  1171.         TraceAI("Normal attack vs: "..MyEnemy)
  1172.         if GetV(V_HOMUNTYPE,MyID) == ELEANOR then
  1173.             MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0)
  1174.             UpdateTimeoutFile()
  1175.         end
  1176.     end
  1177.     -- else
  1178.     if (MySkill ~=0) then
  1179.         TraceAI("Skill Attack: "..MySkill.." target: "..MyEnemy.." level:"..MySkillLevel)
  1180.         SkillTarget=MyEnemy
  1181.         if (MySkill==MA_SHARPSHOOTING and AoEMaximizeTargets==1) then
  1182.             target,hitcount=GetBestFASTarget(MyID)
  1183.             TraceAI(target.." - "..hitcount)
  1184.             if hitcount > 0 and target~=-1 then
  1185.                 SkillTarget=target
  1186.             elseif hitcount== 0 then
  1187.                 MySkill=0
  1188.             end
  1189.             TraceAI("Skill Attack: "..MySkill.." (FAS) target: "..SkillTarget.." enemy: "..MyEnemy)        
  1190.         elseif (MySkill==ML_BRANDISH and AoEMaximizeTargets==1) then
  1191.             target=GetBestBrandishTarget(MyID)
  1192.             if target~=-1 then
  1193.                 SkillTarget=target
  1194.             end
  1195.             TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
  1196.         elseif (MySkill==MH_HAILAGE_STAR or MySkill==MS_BOWLING_BASH) and AoEMaximizeTargets==1 then
  1197.             target=GetBestAoETarget(MyID,MySkill,MySkillLevel)
  1198.             if target~=-1 then
  1199.                 SkillTarget=target
  1200.             end
  1201.             TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
  1202.         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
  1203.             targx,targy=GetBestAoECoord(MyID,MySkill,MySkillLevel)
  1204.             if targx~=-1 then
  1205.                 SkillTargetX,SkillTargetY=targx,targy
  1206.             end
  1207.             TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy)
  1208.         end
  1209.         if MySkill ~=0 then
  1210.             if GetV(V_HOMUNTYPE,MyID) == ELEANOR then
  1211.                 MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0)
  1212.                 UpdateTimeoutFile()
  1213.             end
  1214.             DoSkill(MySkill,MySkillLevel,SkillTarget,-1,SkillTargetX,SkillTargetY)
  1215.         end
  1216.     end
  1217.     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
  1218.         nx,ny=GetV(V_POSITION,MyEnemy)
  1219.         if GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny) >= GetMoveBounds() then
  1220.             logappend("AAI_DANCE","Dance attack canceled, too close to move bounds "..GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny).." "..GetMoveBounds())
  1221.         else
  1222.             logappend("AAI_DANCE","Dancing between "..MyAttackStanceX..","..MyAttackStanceY.." and "..nx..","..ny)
  1223.             Move(MyID,nx,ny)
  1224.         end
  1225.     end
  1226.     MySkill = 0
  1227.     MySkillLevel=0
  1228. end
  1229.  
  1230.  
  1231. -------------------
  1232. -- TANK ROUTINES --
  1233. -------------------
  1234.  
  1235. function    OnTANKCHASE_ST ()
  1236.     if (UseSkillOnly==1) then
  1237.         skill,level=GetAtkSkill(MyID)
  1238.     elseif (UseSkillOnly==-1) then
  1239.         skill,level=GetAtkSkill(MyID)
  1240.         if (GetV(V_SP,MyID)-GetTact(TACT_SP,MyEnemy) < GetSkillInfo(skill,3,level)) then
  1241.             skill,level,sp=nil,nil,nil
  1242.         end
  1243.     else
  1244.         skill,level,sp=nil,nil,nil
  1245.     end
  1246.     TraceAI ("OnTANKCHASE_ST")
  1247.     if (true == IsOutOfSight(MyID,MyEnemy) or (ChaseGiveUpCount > ChaseGiveUp and GetV(V_MOTION,MyID)~=MOTION_MOVE)) then   -- ENEMY_OUTSIGHT_IN
  1248.         if (ChaseGiveUpCount>ChaseGiveUp) then
  1249.             Unreachable[MyEnemy]=1
  1250.         end
  1251.        
  1252.         MyState = IDLE_ST
  1253.         MyEnemy = 0
  1254.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  1255.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  1256.         MyDestX, MyDestY = 0,0
  1257.         TraceAI ("TANKCHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN")
  1258.         ChaseGiveUpCount=0
  1259.        
  1260.         return
  1261.     end
  1262.     if (true == IsInAttackSight(MyID,MyEnemy)) then  -- ENEMY_INATTACKSIGHT_IN
  1263.         ChaseGiveUpCount=0
  1264.         if(IsNotKS(MyID,MyEnemy)==1) then
  1265.             MyState = TANK_ST
  1266.             MySkillUsedCount=0
  1267.             TraceAI ("TANKCHASE_ST -> TANK_ST : ENEMY_INATTACKSIGHT_IN")
  1268.             return OnTANK_ST()
  1269.         else
  1270.             MyState = IDLE_ST
  1271.             MyEnemy = 0
  1272.             EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  1273.             EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  1274.             MyDestX, MyDestY = 0,0
  1275.             TraceAI ("TANKCHASE_ST -> IDLE_ST : Enemy is taken")
  1276.         end
  1277.        
  1278.         return
  1279.     end
  1280.     TraceAI("Tank chase: Can we skill while chasing?")
  1281.     if UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then
  1282.         dist=GetDistanceA(MyID,MyEnemy)
  1283.         tact_debuff,tact_skill,tact_sp,tact_skillclass=GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy)
  1284.         skilltouse={-1,0,0}
  1285.         local SkillList=GetTargetedSkills(MyID)
  1286.         TraceAI("Begin autoskill while tank chasing routine")
  1287.         if (tact_skill < 0) then        -- Negative value of TACT_SKILL -> 1 cast of skill
  1288.             skill_level=tact_skill*-1   -- with level = to the absolute value of the
  1289.             tact_skill=1            -- value of TACT_SKILL.
  1290.         else
  1291.             skill_level=11
  1292.         end
  1293.         for i,v in ipairs(SkillList) do
  1294.  
  1295.             skilltype=v[1]
  1296.             if v[2]~=0 then
  1297.                 --logappend("AAI_Chase","skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass ".. tact_skillclass.."v"..v[1].." "..v[2].." "..v[3])
  1298.                 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
  1299.                 --TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.."v"..v[1].." "..v[2].." "..v[3])
  1300.                     if (skilltype == MOB_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then
  1301.                         if (tact_skillclass == CLASS_MOB) then
  1302.                             if (skilltouse[1] < 2) then
  1303.                                 skilltouse=v
  1304.                             end
  1305.                         end
  1306.                     elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then
  1307.                         if (tact_debuff*-1 == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then
  1308.                             skilltouse=v
  1309.                         end
  1310.                     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
  1311.                         skilltouse=v
  1312.                     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
  1313.                         skilltouse=v
  1314.                     end
  1315.                 end
  1316.             end
  1317.         end
  1318.         if skilltouse[2]~=0 then
  1319.             TraceAI("Using skill while tank chasing:"..skilltouse[2])
  1320.             local slvl=skilltouse[3]
  1321.             if skill_level~=11 then
  1322.                 slvl=skill_level
  1323.             end
  1324.             DoSkill(skilltouse[2],slvl,MyEnemy,-1)
  1325.             if skilltouse[1] == DEBUFF_ATK then
  1326.                 ChaseDebuffUsed=1
  1327.             end
  1328.             MySkillUsedCount=1
  1329.         end
  1330.     else
  1331.         TraceAI("Not in range, and can't use chase skill")
  1332.     end
  1333.     ChaseGiveUpCount=ChaseGiveUpCount+1
  1334.     MyDestX, MyDestY =  GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
  1335.     if MyDestX==-1 or MyDestY==1 then
  1336.         Unreachable[MyEnemy]=1
  1337.         MyState = IDLE_ST
  1338.         MyEnemy = 0
  1339.         EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  1340.         EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  1341.         MyDestX, MyDestY = 0,0
  1342.         TraceAI ("TANKCHASE_ST -> IDLE_ST : target is surrounded so GetStandPoint can't find valid cell. Target dropped and deprioritized")
  1343.         ChaseGiveUpCount=0
  1344.     end
  1345.     if (GetTact(TACT_CHASE,MyEnemy)~=1) then
  1346.         Move (MyID,MyDestX,MyDestY)
  1347.         TraceAI ("TANKCHASE_ST -> TANKCHASE_ST : DESTCHANGED_IN"..MyDestX.." "..MyDestY)
  1348.     end
  1349.     return
  1350. end
  1351.  
  1352. function OnTANK_ST()
  1353.     if (GetV(V_MOTION,MyEnemy)==MOTION_DEAD or IsOutOfSight(MyID,MyEnemy)) then
  1354.         MyState=IDLE_ST
  1355.         TraceAI("TANK_ST->IDLE_ST - Target dead or out of sight")
  1356.         return
  1357.     end
  1358.     if (GetV(V_TARGET,MyEnemy)~=MyID and (TankHitTimeout + 1500) < GetTick()) then
  1359.         TankHitTimeout = GetTick()
  1360.         if (IsInAttackSight(MyID,MyEnemy)==true) then
  1361.             Attack(MyID,MyEnemy)
  1362.         else
  1363.             MyState=TANKCHASE_ST
  1364.             TraceAI("TANK_ST->TANKCHASE_ST - Target out of range")
  1365.         end
  1366.         return
  1367.     end
  1368.     if GetV(V_TARGET,MyEnemy)==MyID then
  1369.         TraceAI("TANK_ST->IDLE_ST - Target is tanked successfully")
  1370.         MyState=IDLE_ST
  1371.     end
  1372. end
  1373.  
  1374. --------------------
  1375. --- REST ROUTINE ---
  1376. --------------------
  1377. function    OnREST_ST ()
  1378.     TraceAI("OnREST_ST")
  1379.     if (DoIdleTasks()==nil) then
  1380.         return
  1381.     end
  1382.     if SuperPassive~=1 then
  1383.         local   object = SelectEnemy(GetFriendTargets())
  1384.         if (object ~= 0) then       --Check for monsters attacking owner
  1385.             MyState = CHASE_ST
  1386.             MyEnemy = object
  1387.             TraceAI ("REST_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
  1388.             return
  1389.         end
  1390.         object = SelectEnemy(GetEnemyList(MyID,0)) 
  1391.         if (object ~= 0) then
  1392.             MyState = CHASE_ST
  1393.             MyEnemy = object
  1394.             TraceAI ("REST_ST -> CHASE_ST : ATTACKED_IN")
  1395.             return
  1396.         end
  1397.     end
  1398.    
  1399.     --If theres nothing else to do, return to the "rest station"
  1400.    
  1401.     x,y=GetV(V_POSITION,MyID)
  1402.     ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  1403.     xoff=x-ox
  1404.     yoff=y-oy
  1405.     TraceAI(xoff.." "..ox.." "..x)
  1406.     if (GetV(V_MOTION,GetV(V_OWNER,MyID))~=MOTION_SIT) then
  1407.         MyState=IDLE_ST
  1408.         TraceAI("REST_ST -> IDLE_ST: Owner stood up")
  1409.     elseif (xoff~=RestXOff or yoff~=RestYOff) then
  1410.         MyDestX=ox+RestXOff
  1411.         MyDestY=oy+RestYOff
  1412.         TraceAI("REST_ST -> REST_ST: moving from "..formatpos(x,y).." to "..formatpos(MyDestX,MyDestY))
  1413.         Move(MyID,MyDestX,MyDestY)
  1414.     else
  1415.         TraceAI("REST_ST -> REST_ST: At rest station")
  1416.     end
  1417. end
  1418.  
  1419. -----------------------------
  1420. ---Friend motion routines ---
  1421. -----------------------------
  1422. function    OnFRIEND_CIRCLE_ST()
  1423.     x,y = GetV(V_POSITION,MyID)
  1424.     dist = GetDistance(x,y,NewFriendX,NewFriendY)
  1425.     TraceAI("OnFRIEND_CIRCLE_ST"..x..","..y.." "..dist)
  1426.     if FriendCircleIter == 0 then
  1427.         if dist > 2 then
  1428.             Move(MyID,NewFriendX,NewFriendY)
  1429.             FriendCircleTimeout=FriendCircleTimeout+1
  1430.             if FriendCircleTimeout > 16 then
  1431.                 MyState=IDLE_ST
  1432.                 TraceAI("Dropping circle attempt, timeout exceeded")
  1433.             end
  1434.             return
  1435.         else
  1436.             FriendCircleIter=1
  1437.         end
  1438.     end
  1439.     if FriendCircleIter ==1 then
  1440.         if x==NewFriendX and y==NewFriendY-2 then
  1441.             FriendCircleIter=2
  1442.             FriendCircleTimeout=0
  1443.         else
  1444.             Move(MyID,NewFriendX,NewFriendY-2)
  1445.             FriendCircleTimeout=FriendCircleTimeout+1
  1446.             if FriendCircleTimeout>4 then
  1447.                 FriendCircleIter=2
  1448.                 FriendCircleTimeout=0
  1449.                 TraceAI("giving up on circle step 1")  
  1450.             end
  1451.             return
  1452.         end
  1453.     end
  1454.     if FriendCircleIter ==2 then
  1455.         if x==NewFriendX-2 and y==NewFriendY then
  1456.             FriendCircleIter=3
  1457.             FriendCircleTimeout=0
  1458.         else
  1459.             Move(MyID,NewFriendX-2,NewFriendY)
  1460.             FriendCircleTimeout=FriendCircleTimeout+1
  1461.             if FriendCircleTimeout>4 then
  1462.                 FriendCircleIter=3
  1463.                 FriendCircleTimeout=0
  1464.                 TraceAI("giving up on circle step 2")  
  1465.             end
  1466.             return
  1467.         end
  1468.     end
  1469.     if FriendCircleIter ==3 then
  1470.         if x==NewFriendX and y==NewFriendY+2 then
  1471.             FriendCircleIter=4
  1472.             FriendCircleTimeout=0
  1473.         else
  1474.             Move(MyID,NewFriendX,NewFriendY+2)
  1475.             FriendCircleTimeout=FriendCircleTimeout+1
  1476.             if FriendCircleTimeout>4 then
  1477.                 FriendCircleIter=4
  1478.                 FriendCircleTimeout=0
  1479.                 TraceAI("giving up on circle step 3")  
  1480.             end
  1481.             return
  1482.         end
  1483.     end
  1484.     if FriendCircleIter ==4 then
  1485.         if x==NewFriendX+2 and y==NewFriendY then
  1486.             FriendCircleIter=5
  1487.             FriendCircleTimeout=0
  1488.         else
  1489.             Move(MyID,NewFriendX+2,NewFriendY)
  1490.             FriendCircleTimeout=FriendCircleTimeout+1
  1491.             if FriendCircleTimeout>4 then
  1492.                 FriendCircleIter=5
  1493.                 FriendCircleTimeout=0
  1494.                 TraceAI("giving up on circle step 4")  
  1495.             end
  1496.             return
  1497.         end
  1498.     end
  1499.     if FriendCircleIter == 5 then
  1500.         if x==NewFriendX and y==NewFriendY-2 then
  1501.             FriendCircleIter=0
  1502.             FriendCircleTimeout=0
  1503.             MyState=IDLE_ST
  1504.             TraceAI("Circle completed")
  1505.             return
  1506.         else
  1507.             Move(MyID,NewFriendX,NewFriendY-2)
  1508.             FriendCircleTimeout=FriendCircleTimeout+1
  1509.             if FriendCircleTimeout>4 then
  1510.                 FriendCircleIter=0
  1511.                 FriendCircleTimeout=0
  1512.                 MyState=IDLE_ST
  1513.                 TraceAI("giving up on circle step 5")  
  1514.             end
  1515.             return
  1516.         end
  1517.     end
  1518. end
  1519.  
  1520. function    OnFRIEND_CROSS_ST()
  1521.     x,y = GetV(V_POSITION,MyID)
  1522.     dist = GetDistance(x,y,NewFriendX,NewFriendY)
  1523.     TraceAI("OnFRIEND_CROSS_ST"..x..","..y.." "..dist)
  1524.     if FriendCircleIter == 0 then
  1525.         if dist > 2 then
  1526.             Move(MyID,NewFriendX,NewFriendY)
  1527.             FriendCircleTimeout=FriendCircleTimeout+1
  1528.             if FriendCircleTimeout > 16 then
  1529.                 MyState=IDLE_ST
  1530.                 TraceAI("Dropping cross attempt, timeout exceeded")
  1531.             end
  1532.             return
  1533.         else
  1534.             FriendCircleIter=1
  1535.         end
  1536.     end
  1537.     if FriendCircleIter ==1 then
  1538.         if x==NewFriendX-2 and y==NewFriendY then
  1539.             FriendCircleIter=2
  1540.             FriendCircleTimeout=0
  1541.         else
  1542.             Move(MyID,NewFriendX-2,NewFriendY)
  1543.             FriendCircleTimeout=FriendCircleTimeout+1
  1544.             if FriendCircleTimeout>4 then
  1545.                 FriendCircleIter=2
  1546.                 FriendCircleTimeout=0
  1547.                 TraceAI("giving up on cross step 1")   
  1548.             end
  1549.             return
  1550.         end
  1551.     end
  1552.     if FriendCircleIter ==2 then
  1553.         if x==NewFriendX+2 and y==NewFriendY then
  1554.             FriendCircleIter=3
  1555.             FriendCircleTimeout=0
  1556.         else
  1557.             Move(MyID,NewFriendX+2,NewFriendY)
  1558.             FriendCircleTimeout=FriendCircleTimeout+1
  1559.             if FriendCircleTimeout>4 then
  1560.                 FriendCircleIter=3
  1561.                 FriendCircleTimeout=0
  1562.                 TraceAI("giving up on cross step 2")   
  1563.             end
  1564.             return
  1565.         end
  1566.     end
  1567.     if FriendCircleIter ==3 then
  1568.         if x==NewFriendX-2 and y==NewFriendY then
  1569.             FriendCircleIter=4
  1570.             FriendCircleTimeout=0
  1571.         else
  1572.             Move(MyID,NewFriendX-2,NewFriendY)
  1573.             FriendCircleTimeout=FriendCircleTimeout+1
  1574.             if FriendCircleTimeout>4 then
  1575.                 FriendCircleIter=4
  1576.                 FriendCircleTimeout=0
  1577.                 TraceAI("giving up on cross step 3")   
  1578.             end
  1579.             return
  1580.         end
  1581.     end
  1582.     if FriendCircleIter ==4 then
  1583.         if x==NewFriendX+2 and y==NewFriendY then
  1584.             FriendCircleIter=5
  1585.             FriendCircleTimeout=0
  1586.         else
  1587.             Move(MyID,NewFriendX+2,NewFriendY)
  1588.             FriendCircleTimeout=FriendCircleTimeout+1
  1589.             if FriendCircleTimeout>4 then
  1590.                 FriendCircleIter=5
  1591.                 FriendCircleTimeout=0
  1592.                 TraceAI("giving up on cross step 4")   
  1593.             end
  1594.             return
  1595.         end
  1596.     end
  1597.     if FriendCircleIter == 5 then
  1598.         if x==NewFriendX-2 and y==NewFriendY then
  1599.             FriendCircleIter=0
  1600.             FriendCircleTimeout=0
  1601.             MyState=IDLE_ST
  1602.             TraceAI("Circle completed")
  1603.             return
  1604.         else
  1605.             Move(MyID,NewFriendX-2,NewFriendY)
  1606.             FriendCircleTimeout=FriendCircleTimeout+1
  1607.             if FriendCircleTimeout>4 then
  1608.                 FriendCircleIter=0
  1609.                 FriendCircleTimeout=0
  1610.                 MyState=IDLE_ST
  1611.                 TraceAI("giving up on cross step 5")   
  1612.             end
  1613.             return
  1614.         end
  1615.     end
  1616. end
  1617.  
  1618. -------------------
  1619. --Command State Process
  1620. -------------------
  1621.  
  1622. function    OnMOVE_CMD_ST ()
  1623.  
  1624.     TraceAI ("OnMOVE_CMD_ST")
  1625.     if GetDistanceAPR(GetV(V_OWNER,MyID),MyMoveX,MyMoveY) > 15 then
  1626.         TraceAI("OnMOVE_CMD_ST -> IDLE_ST: Attempt to move to location off screen")
  1627.         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))))
  1628.         MyState=IDLE_ST
  1629.         return OnIDLE_ST()
  1630.     end
  1631.     local x, y = GetV (V_POSITION,MyID)
  1632.     if (x == MyMoveX and y == MyMoveY) then
  1633.         StickyX,StickyY=0,0
  1634.         if (MoveSticky ~= 0) then
  1635.             if (ReturnToMoveHold==0) then
  1636.                 ReturnToMoveHold = 1
  1637.                 StickyX,StickyY=x,y
  1638.             else
  1639.                 ReturnToMoveHold = 0
  1640.             end
  1641.         end
  1642.         TraceAI("OnMOVE_CMD_ST -> IDLE_ST: Arrived at Destination "..formatpos(x,y))
  1643.         MyState=IDLE_ST
  1644.         return OnIDLE_ST()
  1645.     elseif GetV(V_MOTION,MyID) == MOTION_STAND or MyDestX~=MyMoveX or MyDestY~=MyMoveY then
  1646.         MyDestX,MyDestY=MyMoveX,MyMoveY
  1647.         Move(MyID,MyDestX,MyDestY)
  1648.     end
  1649. end
  1650.  
  1651. function    OnMOVE_CMD_HOLD_ST ()
  1652.    
  1653.     TraceAI ("OnMOVE_CMD_HOLD_ST")
  1654.     MySkillUsedCount        = 0
  1655.     ChaseGiveUpCount        = 0
  1656.     AttackGiveUpCount       = 0
  1657.     ChaseDebuffUsed         = 0
  1658.     AttackDebuffUsed        = 0
  1659.     BypassKSProtect         = 0
  1660.     BerserkMode         = 0
  1661.     if (DoIdleTasks()==nil) then
  1662.         return
  1663.     end
  1664.     if (MoveStickyFight==1 and SuperPassive~=1 ) then
  1665.         local   object = SelectEnemy(GetFriendTargets())
  1666.         if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN
  1667.             MyState = CHASE_ST
  1668.             MyEnemy = object
  1669.             TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
  1670.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  1671.                 OnCHASE_ST()
  1672.             end
  1673.             return
  1674.         end
  1675.         object = SelectEnemy(GetEnemyList(MyID,0))
  1676.         if (object ~= 0) then                           -- ATTACKED_IN
  1677.             MyState = CHASE_ST
  1678.             MyEnemy = object
  1679.             TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : ATTACKED_IN")
  1680.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  1681.                 OnCHASE_ST()
  1682.             end
  1683.             return
  1684.         end
  1685.         object = SelectEnemy(GetEnemyList(MyID,-1))
  1686.         if (object ~= 0) then
  1687.             MyState = TANKCHASE_ST
  1688.             MyEnemy = object
  1689.             TraceAI ("MOVE_CMD_HOLD_ST -> TANKCHASE_ST")
  1690.             return
  1691.         end
  1692.     end
  1693. end
  1694.  
  1695.  
  1696. function OnSTOP_CMD_ST ()
  1697.  
  1698.  
  1699. end
  1700.  
  1701.  
  1702.  
  1703.  
  1704. function OnATTACK_OBJECT_CMD_ST ()
  1705.  
  1706.    
  1707.  
  1708. end
  1709.  
  1710.  
  1711. function OnATTACK_AREA_CMD_ST ()
  1712.  
  1713.     TraceAI ("OnATTACK_AREA_CMD_ST")
  1714.  
  1715.     local   object = GetOwnerEnemy (MyID)
  1716.     if (object == 0) then                          
  1717.         object = GetMyEnemy (MyID)
  1718.     end
  1719.  
  1720.     if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN or ATTACKED_IN
  1721.         MyState = CHASE_ST
  1722.         MyEnemy = object
  1723.         return
  1724.     end
  1725.  
  1726.     local x , y = GetV (V_POSITION,MyID)
  1727.     if (x == MyDestX and y == MyDestY) then         -- DESTARRIVED_IN
  1728.             MyState = IDLE_ST
  1729.     end
  1730.  
  1731. end
  1732.  
  1733.  
  1734.  
  1735.  
  1736. function OnPATROL_CMD_ST ()
  1737.  
  1738.     TraceAI ("OnPATROL_CMD_ST")
  1739.  
  1740.     local   object = GetOwnerEnemy (MyID)
  1741.     if (object == 0) then                          
  1742.         object = GetMyEnemy (MyID)
  1743.     end
  1744.  
  1745.     if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN or ATTACKED_IN
  1746.         MyState = CHASE_ST
  1747.         MyEnemy = object
  1748.         TraceAI ("PATROL_CMD_ST -> CHASE_ST : ATTACKED_IN")
  1749.         return
  1750.     end
  1751.  
  1752.     local x , y = GetV (V_POSITION,MyID)
  1753.     if (x == MyDestX and y == MyDestY) then         -- DESTARRIVED_IN
  1754.         MyDestX = MyPatrolX
  1755.         MyDestY = MyPatrolY
  1756.         MyPatrolX = x
  1757.         MyPatrolY = y
  1758.         Move (MyID,MyDestX,MyDestY)
  1759.     end
  1760.  
  1761. end
  1762.  
  1763.  
  1764. -----------------------------------------------------------------------
  1765. --IF ANYONE READING MY CODE HAS A FUCK'S CLUE WHAT THE HOLD COMMAND IS
  1766. --OR WHAT IN THE DEVIL IT'S PURPOSE IS, PLEASE ENLIGHTEN ME! -Azzy
  1767. ----------------------------------------------------------------------
  1768.  
  1769. function OnHOLD_CMD_ST ()
  1770.  
  1771.     TraceAI ("OnHOLD_CMD_ST")
  1772.     logappend("AAI_ERROR","We're in HOLD_CMD_ST - where did hold cmd come from?")
  1773.     if (MyEnemy ~= 0) then
  1774.         local d = GetDistance(MyEnemy,MyID)
  1775.         if (d ~= -1 and d <= GetV(V_ATTACKRANGE,MyID)) then
  1776.                 Attack (MyID,MyEnemy)
  1777.         else
  1778.             MyEnemy = 0
  1779.             EnemyPosX = {0,0,0,0,0,0,0,0,0,0}
  1780.             EnemyPosY = {0,0,0,0,0,0,0,0,0,0}
  1781.         end
  1782.         return
  1783.     end
  1784.  
  1785.  
  1786.     local   object = GetOwnerEnemy (MyID)
  1787.     if (object == 0) then                          
  1788.         object = GetMyEnemy (MyID)
  1789.         if (object == 0) then                      
  1790.             return
  1791.         end
  1792.     end
  1793.  
  1794.     MyEnemy = object
  1795.  
  1796. end
  1797.  
  1798.  
  1799.  
  1800.  
  1801. function OnSKILL_OBJECT_CMD_ST ()
  1802.     if IsInAttackSight(MyID,MyEnemy,MySkill,MySkillLevel) then
  1803.         DoSkill(MySkill,MySkillLevel,MyEnemy)
  1804.         TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - skill used")
  1805.         MyState=IDLE_ST
  1806.         MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
  1807.         return
  1808.     elseif IsOutOfSight(MyID,MyEnemy) then
  1809.         TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - target off screen")
  1810.         MyState=IDLE_ST
  1811.         MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
  1812.     elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then
  1813.         TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - Couldn't get into range to use skill "..MySkill.." on "..MyEnemy)
  1814.         MyState=IDLE_ST
  1815.         MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0
  1816.         return OnIDLE_ST()
  1817.     else
  1818.         x,y= GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)
  1819.         if x < 10 and y < 10 then
  1820.             local ex,ey=GetV(V_POSITION,MyEnemy)
  1821.             local hx,hy=GetV(V_POSITION,MyID)
  1822.             local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  1823.             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))
  1824.         end
  1825.         Move(MyID,x,y)
  1826.         SkillObjectCMDTimeout=SkillObjectCMDTimeout+1
  1827.         return
  1828.     end
  1829.    
  1830.    
  1831. end
  1832.  
  1833.  
  1834.  
  1835.  
  1836. function OnSKILL_AREA_CMD_ST ()
  1837.  
  1838.     TraceAI ("OnSKILL_AREA_CMD_ST")
  1839.  
  1840.     local x , y = GetV (V_POSITION,MyID)
  1841.     if (GetDistance(x,y,MyDestX,MyDestY) <= AttackRange(MyID,MySkill,MySkillLevel)) then    -- DESTARRIVED_IN
  1842.         DoSkill(MySkill,MySkillLevel,0,nil,MyDestX,MyDestY)
  1843.         MyState = IDLE_ST
  1844.         MySkill = 0
  1845.     else
  1846.         targetx,targety=Closest(MyID,MyDestX,MyDestY,AttackRange(MyID,MySkill,MySkillLevel))
  1847.         TraceAI("Moving to "..formatpos(targetx,targety).." from "..formatpos(x,y))
  1848.         if GetDistanceAPR(GetV(V_OWNER,MyID),targetx,targety) > GetMoveBounds() then
  1849.             MyState = IDLE_ST
  1850.             MySkill = 0
  1851.             TraceAI("OnSKILL_AREA_CMD_ST -> IDLE_ST Target out of range")
  1852.         else
  1853.             Move (MyID,targetx,targety)
  1854.         end
  1855.     end
  1856.  
  1857. end
  1858.  
  1859.  
  1860.  
  1861.  
  1862.  
  1863.  
  1864.  
  1865. function OnFOLLOW_CMD_ST ()
  1866.     TraceAI ("OnFOLLOW_CMD_ST")
  1867.     local d = GetDistanceA (GetV(V_OWNER,MyID),MyID)
  1868.     if ( d > FollowStayBack) then
  1869.         BetterMoveToOwner (MyID,FollowStayBack)
  1870.         return
  1871.     end
  1872.     -- Start the friending process
  1873.     if (StandbyFriending == 1) then
  1874.         local actors = GetActors()
  1875.         for i,v in ipairs(actors) do
  1876.             if (IsMonster(v)~=1 and GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT) then
  1877.                 TraceAI("Friend list modification")
  1878.                 if (IsToRight(GetV(V_OWNER,MyID),v)==1) then
  1879.                     if (MyFriends[v]==nil) then
  1880.                         MyFriends[v] = 1
  1881.                         UpdateFriends()
  1882.                         MyState=FRIEND_CIRCLE_ST
  1883.                         ReturnToState=FOLLOW_CMD_ST
  1884.                         NewFriendX,NewFriendY=GetV(V_POSITION,v)
  1885.                     end
  1886.                 elseif (IsToRight(v,GetV(V_OWNER,MyID))==1) then
  1887.                     if (MyFriends[v]~=nil) then
  1888.                         MyFriends[v] = nil
  1889.                         UpdateFriends()
  1890.                         MyState=FRIEND_CROSS_ST
  1891.                         ReturnToState=FOLLOW_CMD_ST
  1892.                         NewFriendX,NewFriendY=GetV(V_POSITION,v)
  1893.                     end
  1894.                 end
  1895.             end
  1896.         end
  1897.     end
  1898.     -- Okay, that's done.
  1899.     if (DefendStandby == 1 and SuperPassive~=1) then
  1900.         local   object = SelectEnemy(GetFriendTargets())
  1901.         if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN
  1902.             MyState = CHASE_ST
  1903.             MyEnemy = object
  1904.             TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
  1905.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  1906.                 OnCHASE_ST()
  1907.             end
  1908.             return
  1909.         end
  1910.         object = SelectEnemy(GetEnemyList(MyID,0))
  1911.         if (object ~= 0) then                           -- ATTACKED_IN
  1912.             MyState = CHASE_ST
  1913.             MyEnemy = object
  1914.             TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : ATTACKED_IN ")
  1915.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  1916.                 OnCHASE_ST()
  1917.             end
  1918.             return
  1919.         end
  1920.     end
  1921. end
  1922.  
  1923.  
  1924. --#####################################
  1925. --### Targeting Routines start here ###
  1926. --#####################################
  1927. --[[
  1928. targets[actorid]=
  1929.  
  1930. MotionClass
  1931. -1 = Dead
  1932. 0  = Stand
  1933. 1  = Moving
  1934. 2  = Flinch
  1935. 3  = Attacking
  1936. 4  = Skilling
  1937. 5  = Casting
  1938. 6  = Other
  1939.  
  1940. TargetClass
  1941. -2  = Non-friend player
  1942. -1  = Monster
  1943. 0  = None
  1944. 1  = Self
  1945. 2  = Friend/Owner
  1946. ]]--]]
  1947.  
  1948. function GetFriendTargets() -- returns list of targets of friends who are attacking
  1949.     local targets = {}
  1950.     for i,v in ipairs (GetActors()) do
  1951.         if (IsFriend(v) == 1) then
  1952.             motion=GetV(V_MOTION,v)
  1953.             target=GetV(V_TARGET,v)
  1954.             if (IsMonster(target)==1) then
  1955.                 tact=GetTact(TACT_BASIC,target)
  1956.                 if (FriendAttack[motion]==1 and tact > 0 and tact ~=9) then
  1957.                     targets[target]={MotionClassLU[GetV(V_MOTION,target)],GetTargetClass(target),GetTact(TACT_BASIC,target),GetTact(TACT_CAST,target)}
  1958.                    
  1959.                 end
  1960.             end
  1961.         end
  1962.     end
  1963.     return targets
  1964. end
  1965. -- aggro arguments
  1966. -- 2 = snipe
  1967. -- 1 = aggro
  1968. -- 0 = nonaggro
  1969. -- -1 = tank
  1970. -- -2 = rescue
  1971.  
  1972.  
  1973. function    GetEnemyList (myid,aggro)
  1974.     local owner  = GetV(V_OWNER,myid)
  1975.     local enemys = {}
  1976.     if aggro==2 then
  1977.         sskill,slevel=GetSAtkSkill(MyID)
  1978.         oskill,olevel=GetAtkSkill(MyID)
  1979.         mskill,mlevel=GetMinionSkill(MyID)
  1980.     end
  1981.     TraceAI("GetEnemyList with aggro "..aggro)
  1982.     for k,v in pairs(Targets) do
  1983.         tact = GetTact(TACT_BASIC,k)
  1984.         casttact=GetTact(TACT_CAST,k)
  1985.         if tact==TACT_TANKMOB then
  1986.             if GetAggroCount() > AutoMobCount then
  1987.                 tact = TACT_ATTACK_M
  1988.             else
  1989.                 tact = TACT_TANK
  1990.             end
  1991.         end
  1992.         --TraceAI("Target"..k.." tact:"..tact.." Motion"..v[1].." TClass"..v[2])
  1993.         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
  1994.             --TraceAI("Tactics say to attack:"..k)
  1995.             if (IsNotKS(myid,k)==1 and v[1] > -1) then
  1996.                 --TraceAI("Is alive and not a KS")
  1997.                 if aggro == 0 or (aggro~=2 and v[2]>0) then
  1998.                     if (GetMoveBounds() >= GetDistanceRect(owner,k)) then
  1999.                         --TraceAI("Adding to target list: "..k)
  2000.                         r={v[1],v[2],tact,casttact}
  2001.                         enemys[k] = r
  2002.                     --else
  2003.                     --  tempx,tempy=GetV(V_POSITION,owner)
  2004.                     --  targx,targy=GetV(V_POSITION,k)
  2005.                     --  TraceAI("target ignored "..k.." mypos "..tempx..","..tempy.." enemy "..targx..","..targy.." bounds "..GetMoveBounds().."dist "..GetDistanceRect(owner,k))
  2006.                     end
  2007.                 elseif aggro~=2 then
  2008.                     if (GetAggroDist() >= GetDistanceA(owner,k)) then
  2009.                         TraceAI("Adding to target list: "..k)
  2010.                         r={v[1],v[2],tact,casttact}
  2011.                         enemys[k] = r
  2012.                     end
  2013.                 else -- Sniping - so we need to check range on skill, instead of aggrodist
  2014.                     dist = GetDistanceA(MyID,k)
  2015.                     tact_skill_class=(GetTact(TACT_SKILLCLASS,k))
  2016.                     if tact_skill_class==CLASS_MINION and mskill~=0 then
  2017.                         if dist <= GetSkillInfo(mskill,2,mlevel) then
  2018.                             r={v[1],v[2],tact,casttact}
  2019.                             enemys[k] = r
  2020.                         end
  2021.                     elseif sskill~=0 and tact_skill_class~=CLASS_OLD then
  2022.                         if dist <= GetSkillInfo(sskill,2,slevel) then
  2023.                             r={v[1],v[2],tact,casttact}
  2024.                             enemys[k] = r
  2025.                         end
  2026.                     elseif oskill~=0 and tact_skill_class~=CLASS_S then
  2027.                         if dist <= GetSkillInfo(oskill,2,olevel) then
  2028.                             r={v[1],v[2],tact,casttact}
  2029.                             enemys[k] = r
  2030.                         end
  2031.                     end
  2032.                 end
  2033.             end
  2034.         end
  2035.     end
  2036.     return enemys
  2037. end
  2038.  
  2039. -- format of return
  2040. -- enemys[n][1] = Motion Class
  2041. -- enemys[n][2] = Target Class
  2042. -- enemys[n][3] = tact
  2043. -- enemys[n][4] = casttact
  2044.  
  2045. function SelectEnemy(enemys,curenemy)
  2046.     local min_priority=-1
  2047.     local priority
  2048.     local min_dis = 100
  2049.     local dis
  2050.     --local min_aggro = -1
  2051.     --local aggro = 0
  2052.     local result=0
  2053.     local max_reachable=1
  2054.     --local min_mobcount=1
  2055.     --local mobcount=0
  2056.     if curenemy~=nil then -- it's an opportunistic attack
  2057.         local dist = GetDistanceA(MyID,curenemy)
  2058.         local aggrotemp=0
  2059.         if IsFriendOrSelf(GetV(V_TARGET,curenemy)) ==1 then
  2060.             aggrotemp=1
  2061.         end
  2062.         min_priority=convpriority(GetTact(TACT_BASIC,curenemy),aggrotemp)
  2063.         if dist < 3 then
  2064.             return 0
  2065.         else
  2066.             min_dis = dist - 3
  2067.         end
  2068.     end
  2069.     for k,v in pairs(enemys) do
  2070.         local basepriority = v[3] -- basic tact
  2071.         if v[2]>0 and (v[1]==3 or v[4]>=CAST_REACT) then
  2072.             aggro=1
  2073.         else
  2074.             aggro=0
  2075.         end
  2076.         priority=convpriority(basepriority,aggro)
  2077.         --TraceAI(k.." "..basepriority.." "..priority)
  2078.         --elseif ((priority==2 or priority==5) and (v[1]==3 or v[4]>=CAST_REACT)) then
  2079.         --  aggro=-1
  2080.         --elseif then  
  2081.         --aggro = 1
  2082.         --else
  2083.         --  aggro=0
  2084.         --end
  2085.         dis = GetDistanceA (MyID,k)
  2086.         unreachable=Unreachable[k]
  2087.         if unreachable == nil then
  2088.             unreachable=0
  2089.         end
  2090.        
  2091.         --TraceAI(priority.."/"..min_priority.." "..dis.."/"..min_dis.." "..unreachable.."/"..max_reachable)
  2092.         if (unreachable <= max_reachable) then
  2093.             --if (aggro >= min_aggro) then
  2094.                 if (priority > min_priority or (priority==min_priority and dis < min_dis)) then
  2095.                     --if (dis < min_dis) then
  2096.                         result = k
  2097.                         min_dis = dis
  2098.                         min_priority=priority
  2099.                         --min_aggro=aggro
  2100.                         max_reachable=unreachable
  2101.                     --end
  2102.                 end
  2103.             --end
  2104.         end
  2105.     end
  2106.     --TraceAI("SelectEnemy returning target "..result)
  2107.     return result
  2108. end
  2109. function convpriority(base,agr)
  2110.     local priority
  2111.     if base > 9 and base < 13 then --Snipe modes are to be treated as attack
  2112.         base = base-8
  2113.     end
  2114.     if base == 13 then
  2115.         if agr == 1 then
  2116.             base = 7
  2117.         else
  2118.             base = 2
  2119.         end
  2120.     end
  2121.     if base == 14 then
  2122.         base= 1
  2123.     end
  2124.     if base>6 and agr==1 then
  2125.         priority=base
  2126.     elseif base==4 or base==3 or base==15 then
  2127.         priority=base+1
  2128.         if agr==0 then
  2129.             priority=priority-2
  2130.         end
  2131.     elseif (priority==5 and aggro==1) then
  2132.         return 2
  2133.     elseif base==2 then
  2134.         return 1
  2135.     else
  2136.         return 0
  2137.     end
  2138.     return priority
  2139. end
  2140.  
  2141. --####################################################
  2142. --### DoIdleTasks - stuff done in any "idle" state ###
  2143. --### like buffs and command processing            ###
  2144. --####################################################
  2145.  
  2146. function DoIdleTasks()
  2147.     local cmd = List.popleft(ResCmdList)
  2148.     if (cmd ~= nil) then       
  2149.         ProcessCommand (cmd)    -- ¿¹¾à ¸í·É¾î ó¸®
  2150.         return
  2151.     end
  2152.     if OnIdleTasks()==1 then
  2153.         return
  2154.     end
  2155.     if UseAutoHeal==2 then
  2156.         if DoHealingTasks(MyID)==1 then
  2157.             return
  2158.         end
  2159.     end
  2160.     if DoAutoBuffs(1) ~=1 then
  2161.         return
  2162.     end
  2163.     if (UseSacrificeOwner == 1 and SacrificeTimeout ~=-1) then
  2164.         if (GetTick() > SacrificeTimeout) then
  2165.             local skill,level= GetSacrificeSkill(MyID)
  2166.             if (skill <= 0) then
  2167.                 SacrificeTimeout = -1
  2168.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2169.                 DoSkill(skill,level,GetV(V_OWNER,MyID),7)
  2170.                 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.
  2171.                 return
  2172.             end
  2173.         end
  2174.     end
  2175.     if (GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT and MyState~=REST_ST and DoNotUseRest~=1) then
  2176.         MyState=REST_ST
  2177.         TraceAI("DoIdleTasks - Owner sitting, MyState -> REST_ST")
  2178.         return
  2179.     end
  2180.     return 1
  2181. end
  2182.  
  2183. function DoAutoBuffs(buffmode)
  2184.     if GetTick() < AutoSkillTimeout then
  2185.         return 1
  2186.     end
  2187.     if buffmode==2 then -- Berserk mode only buffs
  2188.         if BerserkMode~=1 then
  2189.             return 1
  2190.         end
  2191.     end
  2192.     TraceAI("DoAutoBuffs"..buffmode)
  2193.     if (UseProvokeOwner == buffmode and ProvokeOwnerTimeout ~=-1) then
  2194.         if (GetTick() > ProvokeOwnerTimeout) then
  2195.             local skill,level= GetProvokeSkill(MyID)
  2196.             if (skill <= 0) then
  2197.                 ProvokeOwnerTimeout = -1
  2198.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2199.                 MyBuffSPCosts["ProvokeOwner"]=GetSkillInfo(skill,3,level)
  2200.                 if MyASAPBuffs[3]==skill and buffmode==3 then
  2201.                     ProvokeOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
  2202.                     logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
  2203.                 else
  2204.                     MyPState = MyState
  2205.                     MyState = PROVOKE_ST
  2206.                     MyPEnemy = GetV(V_OWNER,MyID)
  2207.                     MyPSkill = skill
  2208.                     MyPSkillLevel = level
  2209.                     MyPMode = 7
  2210.                     return OnPROVOKE_ST()
  2211.                 end
  2212.             end
  2213.         end
  2214.     end
  2215.     if OffensiveOwnerTimeout ~=-1 then
  2216.         TraceAI("offensive owner ~= -1")
  2217.         if (GetTick() > OffensiveOwnerTimeout) then
  2218.             local skill,level,opt = GetOffensiveOwnerSkill(MyID)
  2219.             TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
  2220.             if (skill <= 0) then
  2221.                 OffensiveOwnerTimeout = -1
  2222.             elseif level==0 or opt~=buffmode then
  2223.                 -- skill in cooldown
  2224.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2225.                 MyBuffSPCosts["OffensiveOwner"]=GetSkillInfo(skill,3,level)
  2226.                 if MyASAPBuffs[3]==skill and buffmode==3 then
  2227.                     OffensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
  2228.                     logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
  2229.                 else
  2230.                     MyPState = MyState
  2231.                     MyState = PROVOKE_ST
  2232.                     MyPEnemy = GetV(V_OWNER,MyID)
  2233.                     MyPSkill = skill
  2234.                     MyPSkillLevel = level
  2235.                     MyPMode = 11
  2236.                     return OnPROVOKE_ST()
  2237.                 end
  2238.             end
  2239.         end
  2240.     end
  2241.     if DefensiveOwnerTimeout ~=-1 then
  2242.         TraceAI("defensive owner ~= -1")
  2243.         if (GetTick() > DefensiveOwnerTimeout)  and (DefensiveBuffOwnerMobbed==0 or (DefensiveBuffOwnerMobbed <= GetAggroCount(GetV(V_OWNER,MyID)) and HPPercent(GetV(V_OWNER,MyID)) < DefensiveBuffOwnerHP)) then
  2244.             local skill,level,opt = GetDefensiveOwnerSkill(MyID)
  2245.             TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
  2246.             if (skill <= 0) then
  2247.                 DefensiveOwnerTimeout = -1
  2248.             elseif level==0 or opt~=buffmode then
  2249.                 -- skill in cooldown
  2250.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2251.                 MyBuffSPCosts["DefensiveOwner"]=GetSkillInfo(skill,3,level)
  2252.                 if MyASAPBuffs[3]==skill and buffmode==3 then
  2253.                     DefensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
  2254.                     logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
  2255.                 else
  2256.                     MyPState = MyState
  2257.                     MyState = PROVOKE_ST
  2258.                     MyPEnemy = GetV(V_OWNER,MyID)
  2259.                     MyPSkill = skill
  2260.                     MyPSkillLevel = level
  2261.                     MyPMode = 12
  2262.                     return OnPROVOKE_ST()
  2263.                 end
  2264.             end
  2265.         end
  2266.     end
  2267.     if OtherOwnerTimeout ~=-1 then
  2268.         TraceAI("other owner ~= -1 "..GetTick().." "..OtherOwnerTimeout)
  2269.         if (GetTick() > OtherOwnerTimeout) then
  2270.             local skill,level,opt = GetOtherOwnerSkill(MyID)
  2271.             TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
  2272.             if (skill <= 0) then
  2273.                 OtherOwnerTimeout = -1
  2274.             elseif level==0 or opt~=buffmode then
  2275.                 -- skill in cooldown
  2276.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2277.                 MyBuffSPCosts["OtherOwner"]=GetSkillInfo(skill,3,level)
  2278.                 if MyASAPBuffs[3]==skill and buffmode==3 then
  2279.                     OtherOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
  2280.                     logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
  2281.                 else
  2282.                     MyPState = MyState
  2283.                     MyState = PROVOKE_ST
  2284.                     MyPEnemy = GetV(V_OWNER,MyID)
  2285.                     MyPSkill = skill
  2286.                     MyPSkillLevel = level
  2287.                     MyPMode = 13
  2288.                     return OnPROVOKE_ST()
  2289.                 end
  2290.             end
  2291.         end
  2292.     end
  2293.    
  2294.     if (UseProvokeSelf == buffmode and ProvokeSelfTimeout ~=-1) then
  2295.         if (GetTick() > ProvokeSelfTimeout) then
  2296.             local skill,level = GetProvokeSkill(MyID)
  2297.             if (skill <= 0) then
  2298.                 ProvokeSelfTimeout = -1
  2299.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2300.                 --MyState=PROVOKE_ST
  2301.                 --OnPROVOKE_ST()
  2302.                 --return
  2303.                 DoSkill(skill,level,MyID)
  2304.                 ProvokeSelfTimeout = GetTick()+GetSkillInfo(skill,8,level)
  2305.                 return
  2306.             end
  2307.         end
  2308.     end
  2309.     if (UseOffensiveBuff == buffmode and QuickenTimeout ~=-1) then
  2310.         if (GetTick() > QuickenTimeout) then
  2311.             local skill,level = GetQuickenSkill(MyID)
  2312.             if (skill<=0) then
  2313.                 QuickenTimeout = -1
  2314.             elseif level==0 then
  2315.                  -- skill in cooldown
  2316.             else
  2317.                 if (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2318.                     DoSkill(skill,level,MyID,2)
  2319.                     QuickenTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2320.                     UpdateTimeoutFile()
  2321.                     return
  2322.                 end
  2323.             end
  2324.         end
  2325.     end
  2326.    
  2327.     if (UseDefensiveBuff == buffmode and GuardTimeout ~=-1) then
  2328.         if (GetTick() > GuardTimeout) then
  2329.             local skill,level = GetGuardSkill(MyID)
  2330.             if (skill <= 0) then
  2331.                 GuardTimeout = -1
  2332.             elseif level==0 then
  2333.                 -- skill in cooldown
  2334.             elseif skill==HAMI_BULWARK and UseSmartBulwark ==1  then
  2335.                 local spreq=MyBuffSPCost+GetSkillInfo(skill,3,level)
  2336.                 if UseOffensiveBuff ~=0 and QuickenTimeout~=-1 then
  2337.                     spreq=spreq+120
  2338.                 end
  2339.                 if spreq <= GetV(V_SP,MyID) then
  2340.                     DoSkill(skill,level,MyID,1)
  2341.                     GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2342.                     UpdateTimeoutFile()
  2343.                 end
  2344.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2345.                 DoSkill(skill,level,MyID,1)
  2346.                 GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2347.                 UpdateTimeoutFile()
  2348.                 return
  2349.             end
  2350.         end
  2351.     end
  2352.     if SOffensiveTimeout ~=-1 then
  2353.         if (GetTick() > SOffensiveTimeout) then
  2354.             local skill,level,opt = GetSOffensiveSkill(MyID)
  2355.             if (skill <= 0) then
  2356.                 SOffensiveTimeout = -1
  2357.             elseif level==0 or opt~=buffmode then
  2358.                 -- skill in cooldown
  2359.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2360.                 MyBuffSPCosts["SOffensive"]=GetSkillInfo(skill,3,level)
  2361.                 DoSkill(skill,level,MyID,4)
  2362.                 SOffensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)     
  2363.                 --logappend("AAI_SKILL",skill.."|"..level.."|"..GetSkillInfo(skill,8,level).."|"..SOffensiveTimeout)
  2364.                 UpdateTimeoutFile()
  2365.                 return
  2366.             end
  2367.         end
  2368.     end
  2369.     if SDefensiveTimeout ~=-1 then
  2370.         TraceAI("sdefensive ~= -1")
  2371.         if (GetTick() > SDefensiveTimeout) then
  2372.             local skill,level,opt = GetSDefensiveSkill(MyID)
  2373.             TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode)
  2374.             if (skill <= 0) then
  2375.                 SDefensiveTimeout = -1
  2376.             elseif level==0 or opt~=buffmode then
  2377.                 -- skill in cooldown
  2378.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2379.                 MyBuffSPCosts["SDefensive"]=GetSkillInfo(skill,3,level)
  2380.                 DoSkill(skill,level,MyID,5)
  2381.                 SDefensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2382.                 UpdateTimeoutFile()
  2383.                 return
  2384.             end
  2385.         end
  2386.     end
  2387.     if SOwnerBuffTimeout ~=-1 then
  2388.         if (GetTick() > SOwnerBuffTimeout) then
  2389.             local skill,level,opt = GetSOwnerBuffSkill(MyID)
  2390.             if (skill <= 0) then
  2391.                 SOwnerBuffTimeout = -1
  2392.             elseif level==0 or opt ~=buffmode then
  2393.                 -- do nothing, skill in cooldown
  2394.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2395.                 MyBuffSPCosts["SOwnerBuff"]=GetSkillInfo(skill,3,level)
  2396.                 DoSkill(skill,level,MyID,6)
  2397.                 SOwnerBuffTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2398.                 UpdateTimeoutFile()
  2399.                 return
  2400.             end
  2401.         end
  2402.     end
  2403.     if UseBayeriSteinWand == buffmode and SteinWandTimeout~=-1 then
  2404.         if GetTick() > SteinWandTimeout  then
  2405.             if GetV(V_HOMUNTYPE,MyID)~=BAYERI then
  2406.                 SteinWandTimeout=-1
  2407.             else
  2408.                 if (GetAggroCount(MyID) >= UseSteinWandSelfMob and UseSteinWandSelfMob~=0) or (GetAggroCount(GetV(V_OWNER,MyID)) >= UseSteinWandOwnerMob and UseSteinWandOwnerMob~=0) then
  2409.                     DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID,10)
  2410.                     SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel)
  2411.                     return
  2412.                 end
  2413.             end
  2414.         end
  2415.     end
  2416.     if (UseAutoMag == buffmode and MagTimeout ~=-1) then
  2417.         if (GetTick() > MagTimeout) then
  2418.             if (GetV(V_MERTYPE,MyID)~=4) then
  2419.                 MagTimeout = -1
  2420.             elseif (40 <= GetV(V_SP,MyID) and level ~=0) then
  2421.                 DoSkill(MER_MAGNIFICAT,1,MyID,3)
  2422.                 MagTimeout = GetTick() + 34000
  2423.                 UpdateTimeoutFile()
  2424.                 return
  2425.             end
  2426.         end
  2427.     end
  2428.     if SightTimeout ~=-1 then
  2429.         TraceAI("tick:"..GetTick().."SightTimeout"..SightTimeout)
  2430.         if (GetTick() > SightTimeout) then
  2431.             local skill,level,opt = GetSightOrAoE(MyID)
  2432.             if (skill <= 0) then
  2433.                 SightTimeout = -1
  2434.             elseif level==0 or opt~=buffmode then
  2435.                 -- skill in cooldown
  2436.             elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then
  2437.                 MyBuffSPCosts["SightOrAoE"]=GetSkillInfo(skill,3,level)
  2438.                 if IsHomun(MyID)==1 then
  2439.                     if MyASAPBuffs[3]==skill and buffmode==3 then
  2440.                         SightTimeout = GetTick()+20000 --We're stuck in an ASAP loop!
  2441.                         logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level))
  2442.                     else
  2443.                         MyPState = MyState
  2444.                         MyState = PROVOKE_ST
  2445.                         MyPEnemy = GetV(V_OWNER,MyID)
  2446.                         MyPSkill = skill
  2447.                         MyPSkillLevel = level
  2448.                         MyPMode = 7
  2449.                         TraceAI("Using AoE skill as buff"..MyPState.." "..MyState.." "..MyPEnemy.." "..MyPSkill.." "..MyPSkillLevel)
  2450.                     return OnPROVOKE_ST()
  2451.                 end            
  2452.                 else
  2453.                     DoSkill(skill,level,MyID,7)
  2454.                     SightTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level)
  2455.                     return
  2456.                 end
  2457.             end
  2458.         end
  2459.     end
  2460.     if PainkillerFriends ~=0 then
  2461.         local skill,level,opt = GetDefensiveOwnerSkill(MyID)
  2462.         opt = PainkillerFriends
  2463.         if skill <=0 then
  2464.             --logappend("AAI_PKF","Painkiller friends disabled, don't have skill")
  2465.             PainkillerFriends=0
  2466.         elseif level==0 or opt~=buffmode then
  2467.             --logappend("AAI_PKF","not time to use it")
  2468.             -- skill in cooldown
  2469.         elseif (GetSkillInfo(skill,3,level) > GetV(V_SP,MyID)) then
  2470.            
  2471.             --logappend("AAI_PKF","No SP")
  2472.             -- no SP
  2473.         else
  2474.             TraceAI("Painkiller Friends check")
  2475.            
  2476.             --logappend("AAI_PKF","Painkiller friends check")
  2477.             for k,v in pairs(Players) do
  2478.                 --logappend("AAI_PKF","Painkiller friend "..k)
  2479.                 if MyFriends[k]==FRIEND or MyFriends[k]==PKFRIEND then
  2480.                     --logappend("AAI_PKF","Painkiller friend "..k.."not a friend")
  2481.                     if PKFriendsTimeout[k]~=nil then
  2482.                         if PKFriendsTimeout[k] < GetTick() then
  2483.                             MyPState = MyState
  2484.                             MyState = PROVOKE_ST
  2485.                             MyPEnemy = k
  2486.                             MyPSkill = skill
  2487.                             MyPSkillLevel = level
  2488.                             MyPMode = k
  2489.                             return OnPROVOKE_ST()
  2490.                         end
  2491.                     else
  2492.                         MyPState = MyState
  2493.                         MyState = PROVOKE_ST
  2494.                         MyPEnemy = k
  2495.                         MyPSkill = skill
  2496.                         MyPSkillLevel = level
  2497.                         MyPMode = k
  2498.                         return OnPROVOKE_ST()
  2499.                     end
  2500.                 end
  2501.             end
  2502.         end
  2503.     end
  2504.     return OnAutoBuffs(buffmode)
  2505. end
  2506.  
  2507. function DoHealingTasks (myid)
  2508.     rhp=HPPercent(myid)
  2509.     ohp=HPPercent(GetV(V_OWNER,myid))
  2510.     skill,level=GetHealingSkill(myid)
  2511.     if skill<=0 then
  2512.         UseAutoHeal=0
  2513.         return
  2514.     elseif level==0 or GetTick() < AutoSkillTimeout then
  2515.         return --skill in cooldown
  2516.     end
  2517.     if rhp < HealSelfHP then
  2518.         if skill==HVAN_CHAOTIC then
  2519.             if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then
  2520.                 TraceAI("Using self Healing skill "..FormatSkill(skill,level))
  2521.                 if ohp < HealOwnerHP and MyState==IDLE_ST then
  2522.                     DoSkill(skill,5,myid)
  2523.                 else
  2524.                     DoSkill(skill,4,myid)
  2525.                 end
  2526.                 return 1
  2527.             else
  2528.                 TraceAI("Can't use self healing skill, no SP")
  2529.             end
  2530.         end
  2531.     end
  2532.     if ohp < HealOwnerHP then
  2533.         if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then
  2534.             TraceAI("Using Healing skill "..FormatSkill(skill,level))
  2535.             DoSkill(skill,level,GetV(V_OWNER,myid))
  2536.             return 1
  2537.         else
  2538.             TraceAI("Can't use healing skill, no SP")
  2539.             return
  2540.         end
  2541.     end
  2542. end
  2543.  
  2544. function UpdateTimeoutFile()
  2545.     if StickyStandby==2 then
  2546.         ShouldStandbyx=ShouldStandby
  2547.     else
  2548.         ShouldStandbyx=0
  2549.     end
  2550.     if IsHomun(MyID)==1 then
  2551.         OutFile=io.open("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua","w")
  2552.     else
  2553.         OutFile=io.open("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua","w")
  2554.     end
  2555.     if OutFile~=nil then
  2556.         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)
  2557.         OutFile:close()
  2558.     else
  2559.         TraceAI("Failed to update timeout file")
  2560.         logappend("AAI_ERROR","Failed to update timeout file for owner "..GetV(V_OWNER,MyID))
  2561.     end
  2562.     return
  2563. end
  2564.  
  2565. function TimeoutConv(a)
  2566.     if a==-1 then
  2567.         return 0
  2568.     else
  2569.         return a
  2570.     end
  2571. end
  2572.  
  2573. function OnPROVOKE_ST()
  2574.     --local skill,level = GetProvokeSkill(MyID)
  2575.     if MyPSkillLevel==nil then
  2576.         logappend("AAI_ERROR","PSkillLevel is nil at start of OnPROVOKE_ST")
  2577.     end
  2578.     if MyPosX[1]==MyPosX[2] and MyPosY[1]==MyPosY[2] then
  2579.         SkillObjectCMDTimeout=SkillObjectCMDTimeout+1
  2580.     end
  2581.     if IsInAttackSight(MyID,MyPEnemy,MyPSkill,MyPSkillLevel) then
  2582.        
  2583.         if MyPMode == 7 then
  2584.             ProvokeOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
  2585.         elseif MyPMode== 9 then
  2586.             SightTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
  2587.         elseif MyPMode== 11 then
  2588.             OffensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
  2589.         elseif MyPMode== 12 then
  2590.             DefensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
  2591.         elseif MyPMode== 13 then
  2592.             OtherOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel)
  2593.         elseif IsPlayer(MyPMode) then -- to detect painkiller-friends, in which case PMode is the id of the friend
  2594.             PKFriendsTimeout[MyPMode]=GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) 
  2595.         end
  2596.         UpdateTimeoutFile()
  2597.         DoSkill(MyPSkill,MyPSkillLevel,MyPEnemy,MyPMode)
  2598.         MyState=MyPState
  2599.         TraceAI("PROVOKE_ST -> IDLE_ST - Buff/AoE okay"..MyState)
  2600.         MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0
  2601.         return
  2602.     elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then
  2603.         TraceAI("PROVOKE_ST -> IDLE_ST Couldn't get into range to provoke/AoE owner")
  2604.         if MyPState~=PROVOKE_ST then
  2605.             MyState=MyPState
  2606.         else
  2607.             MyState=0
  2608.         end
  2609.         SkillObjectCMDTimeout=0
  2610.         MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0
  2611.         return
  2612.     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
  2613.         range = AttackRange(MyID,MyPSkill,MyPSkillLevel)
  2614.         if range > 2 and MyPEnemy==GetV(V_OWNER,MyID) then
  2615.             MoveToOwner(MyID)
  2616.         else
  2617.             --local x,y=ClosestR(MyID,MyPEnemy,AttackRange(MyID,MyPSkill,MyPSkillLevel),math.random(2))
  2618.             local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0)
  2619.             Move(MyID,x,y)
  2620.         end
  2621.         return
  2622.     else
  2623.         local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0)
  2624.         Move(MyID,x,y)
  2625.         return
  2626.     end
  2627. end
  2628.  
  2629. function    OnIDLEWALK_ST ()
  2630.     TraceAI ("OnIDLEWALK_ST")
  2631.     ResetCounters()
  2632.     MyEnemy                 = 0
  2633.     if SPPercent(MyID) < IdleWalkSP then
  2634.         MyState=IDLE_ST
  2635.         TraceAI ("IDLEWALK_ST -> IDLE_ST : SP is below IdleWalkSP - switching to idle mode to regen SP")
  2636.         return OnIDLE_ST()
  2637.     end
  2638.     if (DoIdleTasks()==nil) then
  2639.         return
  2640.     end
  2641.     --Targeting
  2642.     if SuperPassive~=1 then
  2643.         local   object = SelectEnemy(GetFriendTargets())
  2644.         if (object ~= 0) then                           -- MYOWNER_ATTACKED_IN
  2645.             MyState = CHASE_ST
  2646.             MyEnemy = object
  2647.             TraceAI ("IDLEWALK_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
  2648.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  2649.                 OnCHASE_ST()
  2650.             end
  2651.             return
  2652.         end
  2653.         if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then
  2654.             aggro=1
  2655.         else
  2656.             aggro=0
  2657.         end
  2658.         object=SelectEnemy(GetEnemyList(MyID,aggro))
  2659.         if object~=0 then
  2660.             MyState = CHASE_ST
  2661.             MyEnemy = object
  2662.             TraceAI ("IDLEWALK_ST -> CHASE_ST : ATTACKED_IN")
  2663.             if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then
  2664.                 return OnCHASE_ST()
  2665.             end
  2666.             return 
  2667.         end
  2668.         if (aggro==1 and TankMonsterCount < TankMonsterLimit) then
  2669.             object = SelectEnemy(GetEnemyList(MyID,-1))
  2670.             if (object ~= 0) then
  2671.                 MyState = TANKCHASE_ST
  2672.                 MyEnemy = object
  2673.                 TraceAI ("IDLEWALK_ST -> TANKCHASE_ST")
  2674.                 return
  2675.             end
  2676.         end
  2677.     end
  2678.     --Following
  2679.     local distance = GetDistanceAR(MyID,GetV(V_OWNER,MyID))
  2680.     if ( distance > GetMoveBounds()) then       -- MYOWNER_OUTSIGNT_IN
  2681.         MyState = FOLLOW_ST
  2682.         TraceAI ("IDLEWALK_ST -> FOLLOW_ST: Way too far away from owner "..distance)
  2683.         return
  2684.     end
  2685.     if UseAutoHeal==3 then
  2686.         if DoHealingTasks(MyID) == 1 then
  2687.             return
  2688.         end
  2689.     end
  2690.     DoAutoBuffs(-2)
  2691.     -- end of the usual idle tasks - now we have to do the idle walk part of it, which sucks.
  2692.     local x,y=GetV(V_POSITION,MyID)
  2693.     local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  2694.     local motion=GetV(V_MOTION,MyID)
  2695.     if (GetDistanceAPR(MyID,MyDestX,MyDestY)>=1) or IdleWalkTries > 6 or GetDistanceAPR(GetV(V_OWNER,MyID),MyDestX,MyDestY) > GetMoveBounds() then
  2696.         if OldHomunType==AMISTR and UseCastleRoute==1 and (UseIdleWalk==5 or UseIdleWalk==6) and RelativeRoute==0 and GetDistanceP(x,y,ox,oy) > 2 then
  2697.             if GetTick() > AutoSkillTimeout then
  2698.                 DoSkill(HAMI_CASTLE,5,MyID)
  2699.                 TraceAI("Castle Route: Casting castling on self")
  2700.             else
  2701.                 TraceAI("Castle Route: Skill timeout not OK - can't cast castling")
  2702.             end
  2703.             return
  2704.         elseif UseCastleRoute==1 and OldHomunType==AMISTR and RelativeRoute==0 then
  2705.             TraceAI("We're set to use castling route, but can't, UseIdleWalk="..UseIdleWalk.." distance: "..GetDistanceP(x,y,ox,oy))
  2706.         end
  2707.         MyDestX,MyDestY=GetIdleWalkDest(MyID)
  2708.         if MyDestX==ox and MyDestY==oy then
  2709.             MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1)
  2710.         end
  2711.         IdleWalkTries=0
  2712.         TraceAI("IDLEWALK_ST: New destination: "..MyDestX..","..MyDestY)
  2713.         return Move(MyID,MyDestX,MyDestY)
  2714.     elseif motion == MOTION_STAND then
  2715.         if IdleWalkTries == 4 then
  2716.             MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1)
  2717.             TraceAI("IDLEWALK_ST: Having a bit of trouble - adjust dest to: "..MyDestX..","..MyDestY)
  2718.         end
  2719.         TraceAI("IDLEWALK_ST: No move after "..IdleWalkTries.." trying again:"..MyDestX..","..MyDestY)
  2720.         IdleWalkTries=IdleWalkTries+1
  2721.         return Move(MyID,MyDestX,MyDestY)
  2722.     else
  2723.         --we're en route
  2724.         return
  2725.     end
  2726. end
  2727.  
  2728. function GetIdleWalkDest(MyID)
  2729.     local mx,my=GetV(V_POSITION,MyID)
  2730.     local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  2731.     local xoff,yoff=mx-ox,my-oy
  2732.     TraceAI("Idlewalk"..xoff..","..yoff)
  2733.     if UseIdleWalk==1 then --orbit
  2734.         local stepsize = 30
  2735.         if IdleWalkDistance > 4 then
  2736.             stepsize=30
  2737.         else
  2738.             stepsize=45
  2739.         end
  2740.         local temp=math.deg(math.atan2(xoff,yoff))+stepsize/2
  2741.         if temp < 0 then
  2742.             temp=temp+360
  2743.         end
  2744.         local step = math.floor(temp/stepsize) + 1
  2745.         local angle=math.rad(step*stepsize)
  2746.         local destx=math.ceil(math.sin(angle)*IdleWalkDistance)+ox
  2747.         local desty=math.ceil(math.cos(angle)*IdleWalkDistance)+oy
  2748.         TraceAI("Orbit Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my)
  2749.         return destx,desty 
  2750.     elseif UseIdleWalk==2 then -- Cross
  2751.         local temp=math.deg(math.atan2(xoff,yoff))+45
  2752.         if temp < 0 then
  2753.             temp=temp+360
  2754.         end
  2755.         step = math.floor(temp/90)
  2756.         TraceAI("Cross step "..step)
  2757.         if step == 0 then -- north goes to south
  2758.             return ox,oy-IdleWalkDistance
  2759.         elseif step == 1 then -- east goes to north
  2760.             return ox,oy+IdleWalkDistance
  2761.         elseif step == 2 then -- south goes to west
  2762.             return ox-IdleWalkDistance,oy
  2763.         else  -- must be west!
  2764.             return ox+IdleWalkDistance,oy
  2765.         end
  2766.     elseif UseIdleWalk==3 then -- Rectangle
  2767.         local temp=math.deg(math.atan2(xoff,yoff))+22.5
  2768.         local tempt=temp
  2769.         if temp < 0 then
  2770.             temp=temp+360
  2771.         end
  2772.         local step = math.floor(temp/45) + 1
  2773.         local destx,desty = ox,oy
  2774.         if step > 0 and step < 4 then
  2775.             destx = ox + IdleWalkDistance
  2776.         elseif step >4 and step < 8 then
  2777.             destx = ox - IdleWalkDistance
  2778.         end
  2779.         if step ==8 or step == 1 or step == 7 then
  2780.             desty= oy + IdleWalkDistance
  2781.         elseif step > 2 and step < 6 then
  2782.             desty=oy-IdleWalkDistance
  2783.         end
  2784.         TraceAI("Rectangle Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." temp: "..temp.." "..tempt.." step: "..step)
  2785.         return destx,desty
  2786.     elseif UseIdleWalk==4 then -- Random
  2787.         local step = math.random(0,359)
  2788.         local angle=math.rad(step)
  2789.         local destx=absceil(math.sin(angle)*IdleWalkDistance)+ox
  2790.         local desty=absceil(math.cos(angle)*IdleWalkDistance)+oy
  2791.         TraceAI("Random Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my)
  2792.         return destx,desty 
  2793.     elseif UseIdleWalk==5 or UseIdleWalk==6 then -- Route walk
  2794.         local routelen=1
  2795.         local step = nil
  2796.         local dist = 999
  2797.         local posx,posy
  2798.         if RelativeRoute==1 then
  2799.             posx,posy=xoff,yoff
  2800.             TraceAI("Relative route "..xoff..","..yoff)
  2801.         else
  2802.             TraceAI("Absolute route "..mx..","..my)
  2803.             posx,posy=mx,my
  2804.         end
  2805.        
  2806.         for k,v in pairs(MyRoute) do
  2807.             routelen=k
  2808.             if v[1]==posx and v[2]==posy then
  2809.                 step=k
  2810.                 dist=0
  2811.                 TraceAI("Route Analysis: on route cell "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." current step "..step.."/"..dist)
  2812.             else
  2813.                 local distance=math.sqrt((v[1]-posx)^2+(v[2]-posy))
  2814.                 if distance < dist then
  2815.                     dist=distance
  2816.                     step=k
  2817.                 end
  2818.                 TraceAI("Route Analysis: "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." distance "..distance.." current step "..step.."/"..dist)
  2819.             end
  2820.         end
  2821.         -- now we're at position 'step'
  2822.         local nextstep
  2823.         if RouteWalkDirection==1 and step==routelen then
  2824.             if UseIdleWalk==5 then
  2825.                 RouteWalkDirection=-1
  2826.                 nextstep=step-1
  2827.             else
  2828.                 nextstep=1
  2829.             end
  2830.         elseif RouteWalkDirection==-1 and step==1 then
  2831.             if UseIdleWalk==5 then
  2832.                 RouteWalkDirection=1
  2833.                 nextstep=2
  2834.             else
  2835.                 nextstep=routelen
  2836.             end
  2837.         else
  2838.             nextstep=step+1*RouteWalkDirection
  2839.         end
  2840.         TraceAI("Route Walk - nextstep "..nextstep.." from "..step)
  2841.         local destx,desty
  2842.         if RelativeRoute==1 then
  2843.             destx,desty = ox+MyRoute[nextstep][1],oy+MyRoute[nextstep][2]
  2844.         else
  2845.             destx,desty = MyRoute[nextstep][1],MyRoute[nextstep][2]
  2846.         end
  2847.        
  2848.         TraceAI("Route Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." pos: "..posx..","..posy.." routelen: "..routelen.." "..dist.." step: "..step.." nextstep= "..nextstep)
  2849.         return destx,desty
  2850.     else
  2851.         logappend("AAI_ERROR","Invalid UseIdleWalk made it all the way to GetIdleWalkDest()")
  2852.         UseIdleWalk=0
  2853.         return GetV(V_POSITION,MyID)
  2854.     end
  2855. end
  2856. --######################
  2857. --### DoAutoPushback ###
  2858. --######################
  2859.  
  2860. function DoAutoPushback(myid)
  2861.     local actors = GetActors()
  2862.     local x,y=GetV(V_POSITION,myid)
  2863.     for i,v in ipairs(actors) do
  2864.         if (IsMonster(v)==1) then
  2865.             local tact= GetTact(TACT_PUSHBACK,v)
  2866.             local target =GetV(V_TARGET,v)
  2867.             if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then            
  2868.                 TraceAI("Pushback: "..GetDistanceA(target,v))
  2869.                 if (GetDistanceA(target,v) <= AutoPushbackThreshold and GetDistanceA(target,v)~=-1) then
  2870.                     TraceAI("Enemies close to me, using pushback")
  2871.                     local skill,level=GetPushbackSkill(MyID)
  2872.                     if (skill<=0) then
  2873.                         UseAutoPushback=0
  2874.                     elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level) and GetV(V_SKILLATTACKRANGE,MyID,skill) >= GetDistanceA(myid,v)) then
  2875.                         DoSkill(skill,level,v)
  2876.                         return
  2877.                     end
  2878.                     break
  2879.                 end
  2880.             end
  2881.         elseif (IsFriendOrSelf(id)==0 and PVPmode ~=0) then
  2882.             local tact= GetPVPTact(TACT_PUSHBACK,v)
  2883.             local target =GetV(V_TARGET,v)
  2884.             if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then
  2885.                 if (GetDistanceA(target,v) <= AutoPushbackThreshold) then
  2886.                     TraceAI("Enemies close to me, using pushback")
  2887.                     local skill,level=GetPushbackSkill(MyID)
  2888.                     if (skill<=0) then
  2889.                         UseAutoPushback=0
  2890.                     elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level)) then
  2891.                         DoSkill(skill,level,v)
  2892.                         return
  2893.                     end
  2894.                     break
  2895.                 end
  2896.             end
  2897.         end
  2898.     end
  2899.     return 1
  2900. end
  2901.  
  2902.  
  2903. --####################
  2904. --### DoKiteAdjust ###
  2905. --####################
  2906.  
  2907. function DoKiteAdjust(myid,enemy)
  2908.     local step
  2909.     local target=GetV(V_TARGET,enemy)
  2910.     if (IsFriend(target)==1 or target==MyID) then
  2911.         step=KiteStep
  2912.     else
  2913.         step=KiteParanoidStep
  2914.     end
  2915.     local x,y=GetV(V_POSITION,myid)
  2916.     local ox,oy=GetV(V_POSITION,GetV(V_OWNER,myid))
  2917.     local ex,ey=GetV(V_POSITION,enemy)
  2918.     local xoptions ={[2]=1,[0]=1,[1]=1}
  2919.     local yoptions ={[2]=1,[0]=1,[1]=1}
  2920.     local xdirection,ydirection=0,0
  2921.     if (x > ex) then
  2922.         xoptions[2]=0
  2923.     elseif (x < ex) then
  2924.         xoptions[1]=0
  2925.     else
  2926.         yoptions[0]=0
  2927.     end
  2928.     if (y > ey) then
  2929.         yoptions[2]=0
  2930.     elseif (y < ey) then
  2931.         yoptions[1]=0
  2932.     else
  2933.         xoptions[0]=0
  2934.     end
  2935.     if (ox > x) then
  2936.         if (xoptions[1]==1) then
  2937.             xdirection=1
  2938.         elseif (xoptions[0]==1) then
  2939.             xdirection=0
  2940.         elseif (xoptions[2]==1 and (ox-x+step) <= KiteBounds) then
  2941.             xdirection=-1
  2942.         elseif  (ey < y) then
  2943.             xdirection=0
  2944.         else
  2945.             xdirection=1
  2946.         end
  2947.     else
  2948.         if (xoptions[2]==1) then
  2949.             xdirection=-1
  2950.         elseif (xoptions[0]==1) then
  2951.             xdirection=0
  2952.         elseif (xoptions[1]==1 and (x-ox+step) <= KiteBounds) then
  2953.             xdirection=1
  2954.         elseif  (ey > y) then
  2955.             xdirection=0
  2956.         else
  2957.             xdirection=-1
  2958.         end
  2959.     end
  2960.     if (oy > y) then
  2961.         if (yoptions[1]==1) then
  2962.             ydirection=1
  2963.         elseif (yoptions[0]==1 and xdirection~=0) then
  2964.             ydirection=0
  2965.         elseif (yoptions[2]==1 and oy-y+step <= step) then
  2966.             ydirection=-1
  2967.         elseif  (ex > x and xdirection~=0) then
  2968.             ydirection=0
  2969.         else
  2970.             ydirection=1
  2971.         end
  2972.     else
  2973.         if (yoptions[2]==1) then
  2974.             ydirection=-1
  2975.         elseif (yoptions[0]==1 and xdirection~=0) then
  2976.             ydirection=0
  2977.         elseif (yoptions[1]==1 and y-oy+step <= KiteBounds) then
  2978.             ydirection=1
  2979.         elseif  (ex < x and xdirection~=0) then
  2980.             ydirection=0
  2981.         else
  2982.             ydirection=-1
  2983.         end
  2984.     end
  2985.     TraceAI("Kiteing in "..xdirection..","..ydirection.." direction")
  2986.     MyDestX=x+step*xdirection
  2987.     MyDestY=y+step*ydirection
  2988.     Move(myid,MyDestX,MyDestY)
  2989. end
  2990.  
  2991. function FailSkillUse(mode)
  2992.     local modex=mode
  2993.     if IsPlayer(mode)==1 then
  2994.         mode=13
  2995.     end
  2996.     if SkillFailCount[mode]==nil then
  2997.         SkillFailCount[mode]=0
  2998.     end
  2999.     if SkillFailCount[mode] < SkillRetryLimit[mode] then
  3000.         if mode == -1 then -- attack state
  3001.             MySkillUsedCount=math.max(0,MySkillUsedCount-1)
  3002.         elseif mode==1 then
  3003.             GuardTimeout=1
  3004.         elseif mode==2 then
  3005.             QuickenTimeout=1
  3006.         elseif mode==3 then
  3007.             MagTimeout=1
  3008.         elseif mode==4 then
  3009.             SOffensiveTimeout=1
  3010.         elseif mode==5 then
  3011.             SDefensiveTimeout=1
  3012.         elseif mode==6 then
  3013.             SOwnerBuffTimeout=1
  3014.         elseif mode==7 then
  3015.             SightTimeout=1
  3016.         elseif mode==9 then
  3017.             ProvokeOwnerTimeout=1
  3018.         elseif mode==10 then
  3019.             SteinWandTimeout = 1
  3020.         elseif mode==11 then
  3021.             OffensiveOwnerTimeout = 1
  3022.         elseif mode==12 then
  3023.             DefensiveOwnerTimeout = 1
  3024.         elseif mode==13 then
  3025.             OtherOwnerTimeout = 1
  3026.         elseif IsPlayer(modex) then
  3027.             --logappend("AAI_PKF","skill failed on"..mode)
  3028.             PKFriendsTimeout[modex]=1
  3029.         else
  3030.             OnFailUnknownMode(mode)
  3031.         end
  3032.         TraceAI("Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again")
  3033.         logappend("AAI_SKILLFAIL","Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again")
  3034.         SkillFailCount[mode]=SkillFailCount[mode]+1
  3035.     else
  3036.         TraceAI("Skill cast appears to have failed, but we're past the retry limit, so screw it: Mode "..mode.." fail count "..SkillFailCount[mode])
  3037.         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])
  3038.         SkillFailCount[mode]=0
  3039.     end
  3040.     AutoSkillTimeout = 1
  3041.     AutoSkillCastTimeout = 1
  3042.     if AutoSkillCooldown[CastSkill]~=nil then
  3043.         AutoSkillCooldown[CastSkill]=1
  3044.     end
  3045.     CastSkill=0
  3046.     CastSkillLevel=0
  3047.     CastSkillMode=0
  3048.     CastSkillTime=0
  3049.     CastSkillState=0   
  3050. end
  3051.  
  3052. --########################
  3053. --### Main AI Function ###
  3054. --########################
  3055.  
  3056. function AI(myid)
  3057.     MyID = myid
  3058.     if LastAITime==GetTick() then --prevent AI from running twice in the same tick.
  3059.         TraceAI("double-AI() call detected, blocked")
  3060.         return
  3061.     else
  3062.         if LastAITime + 400 < GetTick() and LastAITime > 10 then
  3063.             TraceAI("Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago")
  3064.             logappend("AAI_SKILLFAIL", "Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago")
  3065.         end
  3066.         LastAIDelay=GetTick()-LastAITime
  3067.         LastAITime=GetTick()
  3068.         if MyEnemy ~= 0 then
  3069.             local ex,ey=GetV(V_POSITION,MyEnemy)
  3070.             for v=10,2,-1 do
  3071.                 EnemyPosX[v],EnemyPosY[v]=EnemyPosX[v-1],EnemyPosY[v-1]
  3072.             end
  3073.             EnemyPosX[1],EnemyPosY[1]=ex,ey,GetV(V_MOTION,MyID)
  3074.         end
  3075.     end
  3076.     if DoneInit== 0 then
  3077.         doInit(myid)
  3078.     end
  3079.     if AggressiveRelogTracking==1 then
  3080.         local OutFile
  3081.         if IsHomun(MyID)==1 then
  3082.             OutFile=io.open(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua","w")
  3083.         else
  3084.             OutFile=io.open(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua","w")
  3085.         end
  3086.         if OutFile~=nil then
  3087.             OutFile:write("LastAITime_ART="..LastAITime)
  3088.             OutFile:close()
  3089.         else
  3090.             TraceAI("Failed to update time file for Aggressive Relog Tracking")
  3091.             logappend("AAI_ERROR","Failed to update aggressive relog time tracking file for owner "..GetV(V_OWNER,MyID))
  3092.         end
  3093.     end
  3094.     OnAIstart()
  3095.     -- ###AUTOFRIEND###
  3096.     -- Save the ID to a file so counterpart can friend it
  3097.     -- Why is it here instead of at the start?
  3098.     -- Because the client wont tell us our ID until AI() is called :-(
  3099.     if (NeedToDoAutoFriend==1 and NewAutoFriend==1) then
  3100.         TraceAI("Now it's time to do the autofriend")
  3101.         local owner=GetV(V_OWNER,myid)
  3102.         local OutFile
  3103.         if (IsHomun(myid)==1) then
  3104.             OutFile=io.open("AI/USER_AI/data/H_"..owner..".txt","w")
  3105.         else
  3106.             OutFile=io.open("AI/USER_AI/data/M_"..owner..".txt","w")
  3107.         end
  3108.         if OutFile~=nil then
  3109.             OutFile:write (myid)
  3110.             OutFile:close()
  3111.         else
  3112.             TraceAI("Failed to create autofriending file.")
  3113.             logappend("AAI_ERROR","Failed to create autofriending file for owner "..GetV(V_OWNER,MyID))
  3114.         end
  3115.         NeedToDoAutoFriend=0
  3116.     end
  3117.    
  3118.     --###BOOKKEEPING###
  3119.    
  3120.    
  3121.     -- 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).
  3122.  
  3123.     if GuardTimeout-GetTick() > 350000 then
  3124.         logappend("AAI_ERROR","Guard timeout was "..GuardTimeout.." time is "..GetTick())
  3125.         GuardTimeout=1
  3126.     end
  3127.     if MagTimeout-GetTick() > 350000 then
  3128.         logappend("AAI_ERROR","Mag timeout was "..MagTimeout.." time is "..GetTick())
  3129.         MagTimeout=1
  3130.     end
  3131.     if QuickenTimeout-GetTick() > 1205000 then
  3132.         logappend("AAI_ERROR","Quicken timeout was "..QuickenTimeout.." time is "..GetTick())
  3133.         QuickenTimeout=1
  3134.     end
  3135.     if SOffensiveTimeout-GetTick() > 350000 then
  3136.         logappend("AAI_ERROR","Homun S offensive timeout was "..SOffensiveTimeout.." time is "..GetTick())
  3137.         SOffensiveTimeout=1
  3138.     end
  3139.     if SDefensiveTimeout-GetTick() > 350000 then
  3140.         logappend("AAI_ERROR","Homun S defensive timeout was "..SDefensiveTimeout.." time is "..GetTick())
  3141.         SDefensiveTimeout=1
  3142.     end
  3143.     if SOwnerBuffTimeout-GetTick() > 6000000 then
  3144.         logappend("AAI_ERROR","Homun S owner buff timeout was "..SOwnerBuffTimeout.." time is "..GetTick())
  3145.         SOwnerBuffTimeout=1
  3146.     end
  3147.     if OffensiveOwnerTimeout-GetTick() > 6000000 then
  3148.         logappend("AAI_ERROR","Offensive owner buff timeout was "..OffensiveOwnerTimeout.." time is "..GetTick())
  3149.         OffensiveOwnerTimeout=1
  3150.     end
  3151.     if DefensiveOwnerTimeout-GetTick() > 6000000 then
  3152.         logappend("AAI_ERROR","Defensive owner buff timeout was "..DefensiveOwnerTimeout.." time is "..GetTick())
  3153.         DefensiveOwnerTimeout=1
  3154.     end
  3155.     if OtherOwnerTimeout-GetTick() > 6000000 then
  3156.         logappend("AAI_ERROR","Other owner buff timeout was "..OtherOwnerTimeout.." time is "..GetTick())
  3157.         OtherOwnerTimeout=1
  3158.     end
  3159.     if MyMaxSP~=GetV(V_MAXSP,MyID) then
  3160.         AdjustCapriceLevel()
  3161.         MyMaxSP=GetV(V_MAXSP,MyID)
  3162.     end
  3163.     if AIInitTick==0 or AIInitTick==1 or AIInitTick==nil then
  3164.         AIInitTick=GetTick()
  3165.     end
  3166.     FastChangeCount = 0
  3167.     --###For UseSmartBulwark:
  3168.     if UseSmartBulwark==1 then
  3169.         MyBuffSPCost=0
  3170.         for k,v in pairs(MyBuffSPCosts) do
  3171.             MyBuffSPCost=MyBuffSPCost+v
  3172.         end
  3173.     end
  3174.  
  3175.     --###DATA GATHERING###
  3176.     for k,v in pairs(Unreachable) do
  3177.         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
  3178.             Unreachable[k]=nil
  3179.             TraceAI("Marking as reachable"..k)
  3180.         end
  3181.     end
  3182.    
  3183.     -- Position sensing
  3184.     local x,y=GetV(V_POSITION,MyID)
  3185.     local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID))
  3186.     if MyEnemy ~= 0 then
  3187.         local ex,ey=GetV(V_POSITION,MyEnemy)
  3188.     else
  3189.         ex,ey=0,0
  3190.     end
  3191.     --myposlog=""
  3192.     for v=10,2,-1 do
  3193.         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]
  3194.     end
  3195.     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
  3196.     if MyState==PROVOKE_ST then
  3197.         MyASAPBuffs[1]=MyASAPBuffs[2]
  3198.     else
  3199.         MyASAPBuffs[1]=0
  3200.     end
  3201.     --for v=1,10 do
  3202.     --  myposlog=myposlog.." "..MyPosX[v]..","..MyPosY[v]
  3203.     --end
  3204.     --TraceAI("MyPosLog "..myposlog.." x="..x.."y="..y)
  3205.     if x~=MyPosX[2] or y~=MyPosY[2] then
  3206.         --TraceAI("we moved")
  3207.         LastMovedTime=GetTick()
  3208.     end
  3209.     -- Actor list preprocessing
  3210.     local actors=GetActors()
  3211.     Actors={}
  3212.     OldPlayers=Players
  3213.     Players={}
  3214.     Monsters={}
  3215.     Summons={}
  3216.     Retainers={}
  3217.     Targets={}
  3218.     TakenCells={} --OccupiedCellDetection
  3219.     tMobID=""
  3220.     TankMonsterCount=0
  3221.     local seralegionCount=0
  3222.     SeraLegionList=""
  3223.     local seralegionActive=0
  3224.     local seralegionBugged=0
  3225.     for i,v in ipairs(actors) do
  3226.         local x,y = GetV(V_POSITION,v)
  3227.         TakenCells[x.."_"..y]=1
  3228.         if AAIActors[v]~=1 then
  3229.             if IsHomun(myid)==1 then
  3230.                 logappend("AAI_ACTORS","Actor "..v.." type "..GetV(V_HOMUNTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v))
  3231.             else
  3232.                 logappend("AAI_ACTORS","Actor "..v.." mertype "..GetV(V_MERTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v))
  3233.             end
  3234.             AAIActors[v]=1
  3235.         end
  3236.         local x,y = GetV(V_POSITION,v)
  3237.         if GetV(V_HOMUNTYPE,v)==1102 and x==174 and 33==y and GetV(V_MOTION,v)==MOTION_STAND then
  3238.             -- This is the eden group bathory, ignore it.
  3239.         else   
  3240.             if (false == IsOutOfSight(myid,v)) then
  3241.                 Actors[v]=1
  3242.                 --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." in sight")
  3243.                 if GetV(V_HOMUNTYPE,MyID)==SERA then
  3244.                     local actortype=GetV(V_HOMUNTYPE,v)
  3245.                     if (actortype > 2157 and actortype < 2161) then
  3246.                         seralegionCount=seralegionCount+1
  3247.                         SeraLegionList=SeraLegionList..v.." "
  3248.                         if GetV(V_MOTION,v)==MOTION_STAND and GetDistanceAR(MyID,v)>3 then
  3249.                             seralegionBugged=seralegionBugged+1
  3250.                         else
  3251.                             seralegionActive=seralegionActive+1
  3252.                         end
  3253.                     end
  3254.                 end
  3255.                 if (v >= MagicNumber2 and v <= MagicNumber3) then
  3256.                     Players[v]=1
  3257.                     if MyFriends[v]==FRIEND and GetV(V_OWNER,MyID)~=v then --Newly appeared on screen
  3258.                         if OldPlayers[v]~=1 or AggressiveAutofriend then
  3259.                             local newfriendhidfile=io.open("./AI/USER_AI/data/H_"..v..".txt","r")
  3260.                             local newfriendmidfile=io.open("./AI/USER_AI/data/M_"..v..".txt","r")
  3261.                             TraceAI("new friend on screen, checking H_ID"..v)
  3262.                             if newfriendhidfile~=nil then
  3263.                                 TraceAI("h_id found"..v)
  3264.                                 local newfriendhid=newfriendhidfile:read("*line")
  3265.                                 if newfriendhid~=nil then
  3266.                                     TraceAI("h_id: "..newfriendhid)
  3267.                                     MyFriends[newfriendhid+1-1]=RETAINER --crude type conversion
  3268.                                 end
  3269.                                 newfriendhidfile:close()
  3270.                             end
  3271.                             if newfriendmidfile~=nil then
  3272.                             TraceAI("new friend on screen, checking M_ID"..v)
  3273.                                 local newfriendmid=newfriendmidfile:read("*line")
  3274.                                 TraceAI("m_id found"..v)
  3275.                                 if newfriendmid~=nil then
  3276.                                     TraceAI("m_id: "..newfriendmid)
  3277.                                     MyFriends[newfriendmid+1-1]=RETAINER
  3278.                                 end
  3279.                                 newfriendmidfile:close()
  3280.                             end
  3281.                         end
  3282.                     end
  3283.                 elseif IsMonster(v)==1 then
  3284.                     --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." is a monster")
  3285.                     if LiveMobID == 1 and IsHomun(MyID)==1 then
  3286.                         tMobID=tMobID.."MobID["..v.."]="..GetV(V_HOMUNTYPE,v).."\n"
  3287.                     end
  3288.                     Monsters[v]=1
  3289.                     if (v >= MagicNumber) then
  3290.                         Summons[v]=1
  3291.                     end
  3292.                     if (AutoDetectPlant==1 and IsActive[v]~=1 and IsHomun(myid)~=1) then
  3293.                         if (GetV(V_MOTION,v)==MOTION_STAND or GetV(V_MOTION,v)==MOTION_DAMAGE or GetV(V_MOTION,v)==MOTION_DEAD) then
  3294.                             IsActive[v]=0
  3295.                         else
  3296.                             IsActive[v]=1
  3297.                         end
  3298.                     end
  3299.                     if (GetTact(TACT_BASIC,v)==TACT_TANK and GetV(V_TARGET,v)==MyID) then
  3300.                         TankMonsterCount=TankMonsterCount+1
  3301.                     end
  3302.                     motionclass=MotionClassLU[GetV(V_MOTION,v)]
  3303.                     if motionclass==nil then
  3304.                         motionclass=3
  3305.                     end --CHANGE
  3306.                     --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." "..motionclass)
  3307.                     if (motionclass~=-1 and (IsActive[v]==1 or AutoDetectPlant~=1 or IsHomun(myid)==1)) then
  3308.                         --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." target added")
  3309.                         Targets[v]={motionclass,GetTargetClass(GetV(V_TARGET,v))}
  3310.                     end
  3311.                 elseif (v >= MagicNumber) then     
  3312.                     Retainers[v]=1
  3313.                 end
  3314.             end
  3315.         end
  3316.     end
  3317.     if LiveMobID == 1 and IsHomun(MyID)==1 and tMobID ~="" then
  3318.         local midoutfile=io.open(AggressiveRelogPath.."MobID.lua","w")
  3319.         if midoutfile~=nil then
  3320.             midoutfile:write(tMobID)
  3321.             midoutfile:close()
  3322.         else
  3323.             TraceAI("Failed to update MobID - check permissions and/or AggressiveRelogPath.")
  3324.             logappend("AAI_ERROR","Failed to update aggressive relog time tracking file for owner "..GetV(V_OWNER,MyID))
  3325.         end
  3326.     end
  3327.     if LiveMobID == 1 and IsHomun(MyID)==0 then
  3328.         MobID={}
  3329.         if pcall(function () dofile(AggressiveRelogPath.."MobID.lua") end) then
  3330.             -- do nothing
  3331.         else
  3332.             logappend("AAI_ERROR", "Failed to load MobID from "..AggressiveRelogPath.."MobID.lua")
  3333.         end
  3334.     end
  3335.     if GetV(V_HOMUNTYPE,MyID)==SERA then
  3336.         for t=1,4 do
  3337.             SeraLegionTotalHist[t+1]=SeraLegionTotalHist[t]
  3338.             SeraLegionBugHist[t+1]=SeraLegionBugHist[t]
  3339.         end
  3340.         SeraLegionTotalHist[1]=seralegionCount
  3341.         SeraLegionBugHist[1]=seralegionBugged
  3342.         SeraLegionCount=math.max(SeraLegionTotalHist[1],SeraLegionTotalHist[2],SeraLegionTotalHist[3],SeraLegionTotalHist[4],SeraLegionTotalHist[5])
  3343.         SeraLegionBugged=math.min(SeraLegionBugHist[1],SeraLegionBugHist[2],SeraLegionBugHist[3],SeraLegionBugHist[4],SeraLegionBugHist[5])
  3344.         SeraLegionActive=SeraLegionCount-SeraLegionBugged
  3345.         if SeraLegionCount==0 and GetTick() > AutoSkillTimeout and GetTick() < AutoSkillCooldown[MH_SUMMON_LEGION] then
  3346.             --Why are we in cooldown when there are no bugs out? Cast must have failed, or something killed them.
  3347.             AutoSkillCooldown[MH_SUMMON_LEGION]=1
  3348.             TraceAI("Sera Legion: Legion cooldown canceled, bugs not present")
  3349.         elseif SeraLegionBugged > 2 then
  3350.             AutoSkillCooldown[MH_SUMMON_LEGION]=1
  3351.             TraceAI("Sera Legion: Legion cooldown canceled, bugs bugged")
  3352.         end
  3353.         --logappend("AAI_Legion","Sera Legion - "..SeraLegionCount.." bugs out "..SeraLegionActive.." active and "..SeraLegionBugged.." bugged. "..SeraLegionList)
  3354.     end
  3355.     if PVPmode==1 then
  3356.         for k,v in pairs(Players) do
  3357.             --logappend("AAI_PVP",k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
  3358.             if IsHomun(MyID)==1 then
  3359.                 TraceAI("PVP: Player "..k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
  3360.             else
  3361.                 TraceAI("PVP: Player "..k.."  Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k))
  3362.             end
  3363.             if IsMonster(k)==1 then
  3364.                 Targets[k] = {MotionClassLU[GetV(V_MOTION,k)],GetTargetClass(GetV(V_TARGET,k))}
  3365.             end
  3366.         end
  3367.     end
  3368.    
  3369.     --New autofriend routine
  3370.     if (NewAutoFriend==1 and AssumeHomun==1) then
  3371.         friendedOK=0
  3372.         retainercount=0
  3373.         for k,v in pairs(Retainers) do
  3374.             if (k~=MyID) then
  3375.                 retainercount=retainercount+1
  3376.                 if (MyFriends[k]==2) then
  3377.                     friendedOK=1
  3378.                 end
  3379.             end
  3380.         end
  3381.         if (friendedOK==0) then
  3382.             local owner=GetV(V_OWNER,MyID)
  3383.             if (IsHomun(myid)==1) then
  3384.                 InFile=io.open("./AI/USER_AI/data/M_"..owner..".txt","r")
  3385.             else
  3386.                 InFile=io.open("./AI/USER_AI/data/H_"..owner..".txt","r")
  3387.             end
  3388.             if InFile~=nil then
  3389.                 retainerid=InFile:read("*a")
  3390.                 InFile:close()
  3391.                 retainerid=tonumber(retainerid)
  3392.                 if (retainerid ~=nil) then
  3393.                     MyFriends[retainerid]=2
  3394.                 end
  3395.             end
  3396.         end
  3397.     end
  3398.     --SP+Skillfail Watcher
  3399.     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.
  3400.     local sp = GetV(V_SP,MyID)
  3401.     if CastSkill~=0 then
  3402.         local castskillold = CastSkill
  3403.         local castskilloldlvl = CastSkillLevel -- remember these for the SP watcher down below.
  3404.         local skillcastingtime = GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel)*CastTimeRatio
  3405.         logappend("AAI_SKILLFAIL", "Skillfail Watcher active: "..FormatSkill(CastSkill,CastSkillLevel).."mode"..CastSkillMode.."delay "..LastAIDelay.." motion "..GetV(V_MOTION,MyID))
  3406.         if GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel) == 0 then
  3407.             if LastAIDelay > 500 then
  3408.                 --Skill cast successfully
  3409.                 TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3410.                 logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3411.                 clearcastskill=1
  3412.                 LastASAPTargBuffTime    =0
  3413.                 LastASAPTargBuffTarg    =0
  3414.                 LastASAPTargBuffMode    =0
  3415.             elseif GetTick() - CastSkillTime > 1000 then
  3416.                 --Musta failed, we tried to use an instant cast skill 1 second ago and still no sign of it and no sign of delay.
  3417.                 TraceAI("Delay watcher: no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
  3418.                 logappend("AAI_SKILLFAIL", "no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
  3419.                 clearcastskill=2
  3420.             end
  3421.         else
  3422.             if GetV(V_MOTION,MyID)==MOTION_CASTING then
  3423.                 CastSkillState=1
  3424.             elseif CastSkillState>0 then
  3425.                 if LastAIDelay > 220 then
  3426.                     --Skill cast successfully
  3427.                     if CastSkillMode==8 then
  3428.                         if EleanorMode==1 then
  3429.                             EleanorMode=0
  3430.                         else
  3431.                             EleanorMode=1
  3432.                         end
  3433.                         UpdateTimeoutFile()
  3434.                     end
  3435.                     TraceAI("Delay watcher: Skill use successful detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3436.                     logappend("AAI_SKILLFAIL","successful skill use detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3437.                     clearcastskill=1
  3438.                 else
  3439.                     CastSkillState=CastSkillState+1
  3440.                     if CastSkillState > 2 then
  3441.                         local flinch=0
  3442.                         for v=1,10,1 do
  3443.                             if MyMotions[v]==MOTION_DAMAGE then
  3444.                                 flinch=1
  3445.                                 break
  3446.                             end
  3447.                         end
  3448.                         -- Ground targeted skills aren't interruptible!
  3449.                         if (flinch==1 and GetSkillInfo(CastSkill,7,CastSkillLevel)~=2) or skillcastingtime + AutoSkillTimeout + CastSkillTime < GetTick() then
  3450.                             TraceAI("Delay watcher: Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime))
  3451.                             logappend("AAI_SKILLFAIL", "Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime))
  3452.                             clearcastskill=2
  3453.                         elseif skillcastingtime + CastSkillTime > GetTick() then
  3454.                             TraceAI("Delay watcher: Skill use successful - was seen casting, and no sign of interruption, no delay. mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3455.                             logappend("AAI_SKILLFAIL","Skill use successful - was seen casting, and no sign of interruption, no delay. mode"..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3456.                             clearcastskill=1
  3457.                         end
  3458.                     end
  3459.                 end
  3460.             else -- CastSkillState is 0, so the skill hasn't started casting, but just in case it slipped by:
  3461.                 if LastAIDelay > 220 then
  3462.                     --Skill cast successfully
  3463.                     if CastSkillMode==8 then
  3464.                         if EleanorMode==1 then
  3465.                             EleanorMode=0
  3466.                         else
  3467.                             EleanorMode=1
  3468.                         end
  3469.                         UpdateTimeoutFile()
  3470.                     end
  3471.                     TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3472.                     logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3473.                     clearcastskill=1
  3474.                 elseif GetTick() - CastSkillTime > 1000 then
  3475.                     TraceAI("Delay watcher: no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
  3476.                     logappend("AAI_SKILLFAIL", "no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel))
  3477.                     clearcastskill=2
  3478.                 end
  3479.             end
  3480.         end
  3481.         -- sp watcher
  3482.         if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) or MyLastSP - sp == GetSkillInfo(castskillold,3,castskilloldlvl) then
  3483.             --if IsHomun(myid)==0 then
  3484.                 TraceAI("SP watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3485.                 logappend("AAI_SKILLFAIL","successful skill use detected by SP use - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay)
  3486.                 clearcastskill=1
  3487.             --end
  3488.             if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) then
  3489.                 LastSPTime=GetTick()
  3490.             end
  3491.         elseif MyLastSP<= sp then
  3492.             if sp-MyLastSP==RegenTick[1] and RegenTick[1]~=0 then
  3493.                 LastSPTime=GetTick()
  3494.             end
  3495.         end
  3496.     else
  3497.         if sp-MyLastSP == RegenTick[1] and RegenTick[1]~=0 then
  3498.             LastSPTime=GetTick()
  3499.         elseif sp-MyLastSP > 100 then
  3500.             -- do nothing, owner aid-potted it, ignore this
  3501.         elseif sp-MyLastSP > RegenTick[1] or (sp-MyLastSP < RegenTick[1] and sp-MyLastSP > 0)  then
  3502.             LastSPTime=GetTick()
  3503.             if sp-MyLastSP > math.floor(GetV(V_MAXSP,MyID)/100)+3 then -- this could be the correct tick to use
  3504.                 for v=3,2,-1 do
  3505.                     RegenTick[2][v]=RegenTick[2][v-1]
  3506.                 end
  3507.                 RegenTick[2][1]=sp-MyLastSP
  3508.                 local tickcount=0
  3509.                 for k,v in pairs(RegenTick[2]) do
  3510.                     if v~=sp-MyLastSP then
  3511.                         break
  3512.                     end
  3513.                     tickcount=k
  3514.                 end
  3515.                 if tickcount==3 then
  3516.                     RegenTick[1]=sp-MyLastSP
  3517.                     TraceAI("Watcher: New SP Regen tick set: "..RegenTick[1])
  3518.                     logappend("AAI_SKILLFAIL","New SP Regen tick set: "..RegenTick[1])
  3519.                     UpdateTimeoutFile()
  3520.                 end
  3521.             end
  3522.         end
  3523.     end
  3524.     if clearcastskill==1 then
  3525.         SkillFailCount[CastSkillMode]=0
  3526.         CastSkill=0
  3527.         CastSkillLevel=0
  3528.         CastSkillMode=0
  3529.         CastSkillTime=0
  3530.         CastSkillState=0   
  3531.     elseif clearcastskill==2 then
  3532.             FailSkillUse(CastSkillMode)
  3533.     end
  3534.     MyLastSP=sp
  3535.     --###COMMAND PROCESSING###
  3536.     local msg   = GetMsg (myid)         -- command
  3537.     local rmsg  = GetResMsg (myid)      -- reserved command
  3538.    
  3539.     if msg[1] == NONE_CMD then
  3540.         if rmsg[1] ~= NONE_CMD then
  3541.             if List.size(ResCmdList) < 10 then
  3542.                 List.pushright (ResCmdList,rmsg)
  3543.             end
  3544.         end
  3545.     else
  3546.         List.clear (ResCmdList) -- »õ·Î¿î ¸í·ÉÀÌ ÀÔ·ÂµÇ¸é ¿¹¾à ¸í·ÉµéÀº »èÁ¦ÇÑ´Ù.  
  3547.         ProcessCommand (msg)    -- ¸í·É¾î ó¸®
  3548.     end
  3549.    
  3550.    
  3551.     OnAImiddle()
  3552.    
  3553.     --###EMERGENCY HANDLING###
  3554.    
  3555.     -- Avoid Routine
  3556.     if (IsHomun(MyID)==1 and UseAvoid==1 and (GetTick()-AIInitTick) > 5000) then
  3557.         for i,v in ipairs(actors) do
  3558.             if MyAvoid[GetV(V_HOMUNTYPE,v)]==1 then
  3559.                 os.exit()
  3560.             end
  3561.         end
  3562.     end
  3563.     if UseAutoHeal==1 then
  3564.         if DoHealingTasks(MyID) == 1 then
  3565.             return
  3566.         end
  3567.     end
  3568.    
  3569.     -- Prevent from being left behind
  3570.     -- Used only in critical (merc out of MoveBounds) situations
  3571.     -- Otherwise, IDLE_ST handles it
  3572.    
  3573.     local dist2owner=GetDistanceRect(MyID,GetV(V_OWNER,MyID))
  3574.     if (MyState ~=FOLLOW_ST and dist2owner > GetMoveBounds()) then
  3575.         MyState=FOLLOW_ST
  3576.     end
  3577.     --Cancel all action during the spawn invulnerability
  3578.     if (GetTick() < (MyStart + SpawnDelay)) then
  3579.         return
  3580.     end
  3581.     if OldHomunType==AMISTR and UseCastleDefend ==1 then
  3582.         if GetTick() > AutoSkillTimeout then
  3583.             local mytargetweight =0
  3584.             local ownertargetweight =0
  3585.             for k,v in pairs(Targets) do
  3586.                 if v[2]==1 then
  3587.                     mytargetweight=mytargetweight+GetTact(TACT_WEIGHT,k)
  3588.                 elseif v[2]==2 then
  3589.                     if GetV(V_TARGET,k)==GetV(V_OWNER,MyID) then
  3590.                         ownertargetweight=ownertargetweight+GetTact(TACT_WEIGHT,k)
  3591.                     end
  3592.                 end
  3593.             end
  3594.             if CastleDefendThreshold <= ownertargetweight and ownertargetweight > mytargetweight then
  3595.                 DoSkill(HAMI_CASTLE,5,MyID)
  3596.             end
  3597.         end
  3598.     end
  3599.     object = SelectEnemy(GetEnemyList(MyID,-2))
  3600.     if (object~=0 and object ~= MyEnemy and MyState~=FOLLOW_ST) then
  3601.         MyEnemy=object
  3602.         MyState=CHASE_ST
  3603.         TraceAI("RESCUE ACTIVATED - Targeting "..object)
  3604.     end
  3605.     -- New in 1.51 - specialized cast react tactics.
  3606.     for k,v in pairs(Targets) do
  3607.         if v[2]==1 or v[2] == 2 then
  3608.             tactcast= GetTact(TACT_CAST,k)
  3609.             if tactcast > CAST_REACT then
  3610.                 local skill,level=0,0
  3611.                 if tactcast > 1000 then
  3612.                     for ii,vv in ipairs(GetTargetedSkills()) do
  3613.                         if vv[2]==tactcast and vv[3]~=0 and vv[3]~=nil then
  3614.                             skill=vv[2]
  3615.                             level=vv[3]
  3616.                             break
  3617.                         end
  3618.                     end
  3619.                 else -- generic skill response.
  3620.                     for ii,vv in ipairs(GetTargetedSkills()) do
  3621.                         if tactcast==9 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then
  3622.                             skill=vv[2]
  3623.                             level=vv[3]
  3624.                             break
  3625.                         elseif
  3626.                             tactcast==vv[1]-10 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then
  3627.                             skill=vv[2]
  3628.                             level=vv[3]
  3629.                             break
  3630.                         end
  3631.                     end
  3632.                 end
  3633.                 if skill~=0 then
  3634.                     MySkill=skill
  3635.                     MySkillLevel=level
  3636.                     MyEnemy=k
  3637.                     MyState=OnSKILL_OBJECT_CMD_ST
  3638.                     TraceAI("CAST_REACT_(skill) being enabled against target"..k.." with tactic "..tactcast.." with "..FormatSkill(skill,level))
  3639.                     break
  3640.                 end
  3641.             elseif tactcast == CAST_REACT_STIEN then
  3642.                 if GetV(V_HOMUNTYPE,MyID)==BAYERI then
  3643.                     if AutoSkillTimeout < GetTick() and GetSkillInfo(MH_STEINWAND,3,5) < GetV(V_SP,MyID) then
  3644.                         DoSkill(MH_STEINWAND,5,MyID)
  3645.                         TraceAI("CAST_REACT_STIEN being enabled against target"..k.." with tactic "..tactcast)
  3646.                    
  3647.                         return
  3648.                     end
  3649.                 end
  3650.             elseif tactcast == CAST_REACT_CASTLE then
  3651.                 if (modulo(GetV(V_HOMUNTYPE,MyID),4)==AMISTR or OldHomunType==AMISTR) and v[2]==2 then
  3652.                     if AutoSkillTimeout < GetTick() and GetSkillInfo(HAMI_CASTLE,3,5) < GetV(V_SP,MyID) then
  3653.                         DoSkill(HAMI_CASTLE,5,MyID)
  3654.                         TraceAI("CAST_REACT_CASTLE being enabled against target"..k.." with tactic "..tactcast)
  3655.                         return
  3656.                     end
  3657.                 end
  3658.             end
  3659.         end
  3660.     end
  3661.  
  3662.     if MyState~=PROVOKE_ST then
  3663.         if DoAutoBuffs(3) ~= 1 then
  3664.             logappend("AAI_ASAP","ASAP BUFF "..MyState)
  3665.             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
  3666.                 MyASAPBuffs[1]=MyPSkill
  3667.             end
  3668.             return
  3669.         end
  3670.     end
  3671.     -- Check to see if mercenary should activate kiting routine
  3672.     if ((KiteMonsters==1 and (KiteOK(MyID)==1 or ForceKite == 1)) or HPPercent(MyID) < FleeHP) then
  3673.         local x,y=GetV(V_POSITION,MyID)
  3674.         if ((x==KiteDestX and y==KiteDestY) or (KiteDestX==0 and KiteDestY==0)) then
  3675.             local actors = GetActors()
  3676.             kitecalled=0
  3677.             for i,v in ipairs(actors) do
  3678.                 if (IsMonster(v)==1 and GetV(V_MOTION,v) ~= MOTION_DEAD) then
  3679.                     local target=GetV(V_TARGET,v)
  3680.                     local tact = GetTact(TACT_KITE,v)
  3681.                     if ((IsFriendOrSelf(target)==1 and tact==1) or tact==2) then
  3682.                         local threshold
  3683.                         if (IsFriend(target)==1 or target==MyID) then
  3684.                             threshold=KiteThreshold
  3685.                         else
  3686.                             threshold=KiteParanoidThreshold
  3687.                         end
  3688.                         if (GetDistanceA(MyID,v) <= threshold) then
  3689.                             TraceAI("Enemies close to me, start kite routine. ")
  3690.                             DoKiteAdjust(MyID,v)
  3691.                             kitecalled=1
  3692.                             break
  3693.                         end
  3694.                     end
  3695.                 end
  3696.             end
  3697.             if (kitecalled == 0) then
  3698.                 KiteDestX,KiteDestY=0,0
  3699.             end
  3700.         else
  3701.             Move(myid,KiteDestX,KiteDestY)
  3702.         end
  3703.     end
  3704.     --Stienwand autocast on tele stuff
  3705.     if GetTick() < SteinWandPauseTime then
  3706.         return
  3707.     end
  3708.     if UseSteinWandTele ==1 and GetV(V_HOMUNTYPE,MyID)==BAYERI then
  3709.         if SteinWandPauseTime == 0 then
  3710.             DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID)
  3711.             SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel)
  3712.             SteinWandPauseTime=SteinWandTelePause+GetTick()
  3713.             return
  3714.         end
  3715.     end
  3716.     --###STATE PROCESSES###
  3717.     --TraceAI("SP tracking: Time: "..GetTick().." last moved: "..LastMovedTime.." last sp time "..LastSPTime)
  3718.     if (MyState == IDLE_ST) then
  3719.         OnIDLE_ST ()
  3720.     elseif (MyState == CHASE_ST) then                  
  3721.         OnCHASE_ST ()
  3722.     elseif (MyState == ATTACK_ST) then
  3723.         OnATTACK_ST ()
  3724.     elseif (MyState == FOLLOW_ST) then
  3725.         OnFOLLOW_ST ()
  3726.     elseif (MyState == MOVE_CMD_ST) then
  3727.         OnMOVE_CMD_ST ()
  3728.     elseif (MyState == STOP_CMD_ST) then
  3729.         OnSTOP_CMD_ST ()
  3730.     elseif (MyState == ATTACK_OBJECT_CMD_ST) then
  3731.         OnATTACK_OBJECT_CMD_ST ()
  3732.     elseif (MyState == ATTACK_AREA_CMD_ST) then
  3733.         OnATTACK_AREA_CMD_ST ()
  3734.     elseif (MyState == PATROL_CMD_ST) then
  3735.         OnPATROL_CMD_ST ()
  3736.     elseif (MyState == HOLD_CMD_ST) then
  3737.         OnHOLD_CMD_ST ()
  3738.     elseif (MyState == SKILL_OBJECT_CMD_ST) then
  3739.         OnSKILL_OBJECT_CMD_ST ()
  3740.     elseif (MyState == SKILL_AREA_CMD_ST) then
  3741.         OnSKILL_AREA_CMD_ST ()
  3742.     elseif (MyState == FOLLOW_CMD_ST) then
  3743.         OnFOLLOW_CMD_ST ()
  3744.     elseif (MyState == IDLEWALK_ST) then
  3745.         OnIDLEWALK_ST ()
  3746.     elseif (MyState == ORBITWALK_ST) then
  3747.         OnORBITWALK_ST()
  3748.     elseif (MyState == REST_ST) then
  3749.         OnREST_ST()
  3750.     elseif (MyState == TANKCHASE_ST) then
  3751.         OnTANKCHASE_ST()
  3752.     elseif (MyState == TANK_ST) then
  3753.         OnTANK_ST()
  3754.     elseif (MyState == PROVOKE_ST) then
  3755.         OnPROVOKE_ST()
  3756.     elseif (MyState == MOVE_CMD_HOLD_ST) then
  3757.         OnMOVE_CMD_HOLD_ST()
  3758.     elseif (MyState == FRIEND_CROSS_ST) then
  3759.         OnFRIEND_CROSS_ST()
  3760.     elseif (MyState == FRIEND_CIRCLE_ST) then
  3761.         OnFRIEND_CIRCLE_ST()
  3762.     else
  3763.         if NewState(MyState)==-1 then  
  3764.             TraceAI("Invalid State: "..MyState.." -> IDLE_ST")
  3765.             logappend("AAI_ERROR","MyState set to invalid state: "..MyState)
  3766.             MyState=IDLE_ST
  3767.         end
  3768.     end
  3769.     OnAIEnd()
  3770. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement