----------------------------- -- Dr. Azzy's Merc/Homun AI -- Written by Dr. Azzy of iRO Loki -- This AI is intended for use on official servers only -- Permission granted to distribute in unmodified form. -- You may expand the AI freely through the M_Extra and H_Extra files MainVersion="1.52_2" ResCmdList = List.new() -- As of dev 15, global variables are now in Const_.lua AutoSkillCooldown = {} AutoSkillCooldown[HFLI_MOON]=0 AutoSkillCooldown[HVAN_CAPRICE]=0 AutoSkillCooldown[HVAN_CHAOTIC]=0 AutoSkillCooldown[HLIF_HEAL]=0 AutoSkillCooldown[MH_POISON_MIST]=0 AutoSkillCooldown[MH_LAVA_SLIDE]=0 AutoSkillCooldown[MH_STEINWAND]=0 AutoSkillCooldown[MH_SUMMON_LEGION]=0 -----------Config checking---------------- function doInit(myid) if IsHomun(myid) == 0 then -- if the stupid devs made GetV(V_MERTYPE,id) work i wouldnt need this! MercType=GetMerType(myid) end if (UseAttackSkill==0 and UseSkillOnly==1) then UseSkillOnly = 0 end if DanceMinSP < 0 then DanceMinSP=math.floor(GetV(V_MAXSP,MyID)*DanceMinSP/100)*-1 end if ChaseSPPauseSP < 0 then ChaseSPPauseSP=math.floor(GetV(V_MAXSP,MyID)*ChaseSPPauseSP/100)*-1 end MyMaxSP=GetV(V_MAXSP,MyID) MyLastSP=GetV(V_SP,MyID) local loadtimesuccess = pcall(loadtimeouts) if loadtimesuccess==false then logappend("AAI_ERROR","failed to load timeouts for owner "..GetV(V_OWNER,MyID).." if this is the first time you've used this account with AzzyAI, disregard this message") end if GetV(V_SKILLATTACKRANGE,myid,HVAN_CAPRICE) > 1 then -- it was a vani OldHomunType=VANILMIRTH end if GetV(V_SKILLATTACKRANGE,myid,MH_ERASER_CUTTER) == 1 then UseEiraEraseCutter=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_XENO_SLASHER) == 1 then -- UseEiraXenoSlasher=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_STAHL_HORN) == 1 then UseBayeriStahlHorn=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_HEILIGE_STANGE) == 1 then UseBayeriHailegeStar=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_NEEDLE_OF_PARALYZE) == 1 then UseSeraParalyze=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_POISON_MIST) == 1 then UseSeraPoisonMist=0 end if GetV(V_SKILLATTACKRANGE,myid,MH_LAVA_SLIDE) == 1 then UseDieterLavaSlide=0 end if LagReduction ==1 then dofile('./AI/USER_AI/twRO.lua') end local mskill,mlevel=GetMobSkill(MyID) if mskill==0 and (GetV(V_HOMUNTYPE,MyID)==SERA and PoisonMistMode~=0) or (GetV(V_HOMUNTYPE,MyID)==DIETER and LavaSlideMode~=0) then mskill,mlevel=GetSightOrAoE(MyID) end if mskill~=0 and mlevel~=0 and AoEReserveSP==1 and AutoMobMode~=0 then ReserveSP=GetSkillInfo(mskill,3,mlevel) end OnInit() if AggressiveRelogTracking~=1 then MagTimeout=MagTimeout+500 SOffensiveTimeout=SOffensiveTimeout+500 SDefensiveTimeout=SDefensiveTimeout+500 SOwnerBuffTimeout=SOwnerBuffTimeout+500 GuardTimeout=GuardTimeout+500 QuickenTimeout=QuickenTimeout+500 OffensiveOwnerTimeout = OffensiveOwnerTimeout+500 DefensiveOwnerTimeout = DefensiveOwnerTimeout+500 OtherOwnerTimeout = OtherOwnerTimeout+500 else timelag=LastAITime_ART-GetTick() MagTimeout=MagTimeout+timelag SOffensiveTimeout=SOffensiveTimeout+timelag SDefensiveTimeout=SDefensiveTimeout+timelag SOwnerBuffTimeout=SOwnerBuffTimeout+timelag GuardTimeout=GuardTimeout+timelag QuickenTimeout=QuickenTimeout+timelag OffensiveOwnerTimeout = OffensiveOwnerTimeout+timelag DefensiveOwnerTimeout = DefensiveOwnerTimeout+timelag OtherOwnerTimeout = OtherOwnerTimeout+timelag end AdjustCapriceLevel() UpdateTimeoutFile() DoneInit=1 end function AdjustCapriceLevel() local msp=GetV(V_MAXSP,MyID) if msp < 30 and VanCapriceLevel==nil then if msp >=28 then VanCapriceLevel=4 elseif msp >=26 then VanCapriceLevel=3 elseif msp >=24 then VanCapriceLevel=2 else VanCapriceLevel=1 end end end function loadtimeouts() if IsHomun(MyID)==1 then dofile("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua") else dofile("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua") end if AggressiveRelogTracking==1 then if IsHomun(MyID)==1 then dofile(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua") else dofile(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua") end end end --######################################## --### Friend the merc/homun - old one ### --### by Misch, new one by Dr. Azzy ### --######################################## if (AssumeHomun==1) then if (NewAutoFriend==0) then ff = {} Hactors = GetActors() Howner = GetV(V_OWNER,Hactors[1]) FX = 0 FY = 0 OX,OY = GetV(V_POSITION,Howner) for i,v in ipairs(Hactors) do if (v ~= Howner) then if (IsMonster(v)==0) then FX,FY = GetV(V_POSITION,v) if (FX==OX and FY==OY and v<100000) then --is not a player MyFriends[v]=2 end end end end else NeedToDoAutoFriend=1 TraceAI("Setting NeedToDoAutoFriend") end end --#################################### --######## Command Processing ######## --#################################### function OnMOVE_CMD (x,y) TraceAI ("OnMOVE_CMD") ResetCounters() if ( x == MyDestX and y == MyDestY and MOTION_MOVE == GetV(V_MOTION,MyID)) then return end --local curX, curY = GetV (V_POSITION,MyID) --if (math.abs(x-curX)+math.abs(y-curY) > 15) then -- List.pushleft (ResCmdList,{MOVE_CMD,x,y}) -- x = math.floor((x+curX)/2) -- y = math.floor((y+curY)/2) --end MyMoveX,MyMoveY=x,y MyDestX,MyDestY=x,y Move (MyID,MyDestX,MyDestY) MyState = MOVE_CMD_ST if (MirAIFriending==1) then --emulate MirAI's annoying friending process TraceAI("Starting Friend Routine") local actors = GetActors() for i,v in ipairs(actors) do if (IsMonster(v)~=1 and v~=GetV(V_OWNER,MyID) and v~=MyID) then xx,yy=GetV(V_POSITION,v) if (xx==x and (yy+1==y or yy-1==y)) then if (MyFriends[v]==nil) then MyFriends[v] = 1 UpdateFriends() MyState=FRIEND_CIRCLE_ST TraceAI("Friend Addition") NewFriendX,NewFriendY=GetV(V_POSITION,v) return elseif (MyFriends[v]~=nil) then MyFriends[v] = nil UpdateFriends() MyState=FRIEND_CROSS_ST TraceAI("Friend Removal") NewFriendX,NewFriendY=GetV(V_POSITION,v) return end end end end end end function OnSTOP_CMD () TraceAI ("OnSTOP_CMD") logappend("AAI_ERROR","STOP_CMD sent! This should NEVER HAPPEN!") if (GetV(V_MOTION,MyID) ~= MOTION_STAND) then Move (MyID,GetV(V_POSITION,MyID)) end MyState = IDLE_ST MyDestX = 0 MyDestY = 0 MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkill = 0 end function OnATTACK_OBJECT_CMD (id) TraceAI ("OnATTACK_OBJECT_CMD") ResetCounters() MySkill = 0 MyEnemy = id BypassKSProtect=1 if (UseBerserkAttack==1) then BerserkMode=1 end MyState = CHASE_ST OnCHASE_ST() end function OnATTACK_AREA_CMD (x,y) TraceAI ("OnATTACK_AREA_CMD") logappend("AAI_ERROR","ATTACK_AREA_CMD sent! This should NEVER HAPPEN!") if (x ~= MyDestX or y ~= MyDestY or MOTION_MOVE ~= GetV(V_MOTION,MyID)) then Move (MyID,x,y) end MyDestX = x MyDestY = y MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyState = ATTACK_AREA_CMD_ST end function OnPATROL_CMD (x,y) TraceAI ("OnPATROL_CMD") logappend("AAI_ERROR","PATROL_CMD sent! This should NEVER HAPPEN!") MyPatrolX , MyPatrolY = GetV (V_POSITION,MyID) MyDestX = x MyDestY = y Move (MyID,x,y) MyState = PATROL_CMD_ST end function OnHOLD_CMD () TraceAI ("OnHOLD_CMD") logappend("AAI_ERROR","HOLD_CMD sent! This should NEVER HAPPEN!") MyDestX = 0 MyDestY = 0 MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyState = HOLD_CMD_ST end function OnSKILL_OBJECT_CMD (level,skill,id) TraceAI ("OnSKILL_OBJECT_CMD"..skill.." "..id.." "..level) ResetCounters() if skill==MH_PAIN_KILLER and IsPlayer(id)==1 and PainkillerFriends~=0 and id~=GetV(V_OWNER,MyID) then MyPState = MyState MyState = PROVOKE_ST MyPEnemy = id MyPSkill = skill MyPSkillLevel = level MyPMode = id if MyFriends[id]~=FRIEND then MyFriends[id]=PKFRIEND if PainkillerFriendsSave==1 then UpdateFriends() end end return OnPROVOKE_ST() else MySkillLevel = level MySkill = skill MyEnemy = id if IsMonster(id)==1 and SuperPassive~=1 then BypassKSProtect=1 if (UseBerserkSkill==1) then BerserkMode=1 end MyState = CHASE_ST OnCHASE_ST() else MyState = SKILL_OBJECT_CMD_ST end end end function OnSKILL_AREA_CMD (level,skill,x,y) ResetCounters() TraceAI ("OnSKILL_AREA_CMD") targetx,targety=Closest(MyID,x,y,AttackRange(MyID,skill,level)) Move (MyID,targetx,targety) MyDestX = x MyDestY = y MySkillLevel = level MySkill = skill MyState = SKILL_AREA_CMD_ST end function OnFOLLOW_CMD () ReturnToMoveHold = 0 if (MyState ~= FOLLOW_CMD_ST) then if StickyStandby > 0 then ShouldStandby=1 end BetterMoveToOwner (MyID,FollowStayBack) MyState = FOLLOW_CMD_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkill = 0 TraceAI ("OnFOLLOW_CMD") else if StickyStandby > 0 then ShouldStandby=0 end MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkill = 0 TraceAI ("FOLLOW_CMD_ST --> IDLE_ST") end end function ProcessCommand (msg) if (msg[1] == MOVE_CMD) then TraceAI ("MOVE_CMD") OnMOVE_CMD (msg[2],msg[3]) elseif (msg[1] == STOP_CMD) then TraceAI ("STOP_CMD") OnSTOP_CMD () elseif (msg[1] == ATTACK_OBJECT_CMD) then TraceAI ("ATTACK_OBJECT_CMD") OnATTACK_OBJECT_CMD (msg[2]) elseif (msg[1] == ATTACK_AREA_CMD) then TraceAI ("ATTACK_AREA_CMD") OnATTACK_AREA_CMD (msg[2],msg[3]) elseif (msg[1] == PATROL_CMD) then TraceAI ("PATROL_CMD") OnPATROL_CMD (msg[2],msg[3]) elseif (msg[1] == HOLD_CMD) then TraceAI ("HOLD_CMD") OnHOLD_CMD () elseif (msg[1] == SKILL_OBJECT_CMD) then TraceAI ("SKILL_OBJECT_CMD") OnSKILL_OBJECT_CMD (msg[2],msg[3],msg[4],msg[5]) elseif (msg[1] == SKILL_AREA_CMD) then TraceAI ("SKILL_AREA_CMD") OnSKILL_AREA_CMD (msg[2],msg[3],msg[4],msg[5]) elseif (msg[1] == FOLLOW_CMD) then TraceAI ("FOLLOW_CMD") OnFOLLOW_CMD () end end function ResetCounters() MyPState = 0 MyPSkill = 0 MyPEnemy = 0 MyPSkillLevel = 0 MySkillUsedCount = 0 ChaseGiveUpCount = 0 AttackGiveUpCount = 0 ChaseDebuffUsed = 0 AttackDebuffUsed = 0 BypassKSProtect = 0 BerserkMode = 0 ReturnToState = 0 NewFriend = 0 FriendMotionTime = 0 FriendCircleIter = 0 FriendCircleTimeout = 0 AtkPosbugFixTimeout = 0 SkillObjectCMDTimeout = 0 FollowTryCount = 0 MyMoveX,MyMoveY = 0,0 if CastSkillMode < 0 then CastSkill=0 CastSkillLevel=0 CastSkillMode=0 end return end --############################### --######## State Process ######## --############################### function OnIDLE_ST () --if ReturnToMoveHold~=0 then -- MyState=MOVE_CMD_HOLD_ST -- OnMOVE_CMD_HOLD_ST() -- return --end TraceAI ("OnIDLE_ST") ResetCounters() MySkill = 0 MyDestX = 0 MyDestY = 0 MyEnemy = 0 if (DoIdleTasks()==nil) then return end --StickyStandby handling if (ShouldStandby==1 and StickyStandby > 0) then MyState=FOLLOW_CMD_ST return end --Targeting if SuperPassive~=1 then local object = SelectEnemy(GetFriendTargets()) if (object ~= 0) then -- MYOWNER_ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0) ) then aggro=1 else aggro=0 end object=SelectEnemy(GetEnemyList(MyID,aggro)) if object~=0 then MyState = CHASE_ST MyEnemy = object TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then return OnCHASE_ST() end return end if (aggro==1 and TankMonsterCount < TankMonsterLimit) then object = SelectEnemy(GetEnemyList(MyID,-1)) if (object ~= 0) then MyState = TANKCHASE_ST MyEnemy = object TraceAI ("IDLE_ST -> TANKCHASE_ST") return end end end --Following local distance if ReturnToMoveHold ~=0 then distance = GetDistanceAP(MyID,StickyX,StickyY) else distance = GetDistanceFromOwner(MyID) end if (UseIdleWalk~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP)) then -- CHECK if ( distance > GetMoveBounds() or distance == -1) then -- MYOWNER_OUTSIGNT_IN MyState = FOLLOW_ST TraceAI ("IDLE_ST -> FOLLOW_ST") return else TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk) MyState=IDLEWALK_ST end elseif (( distance > DiagonalDist(FollowStayBack+1) and not (ChaseSPPause==1 and GetV(V_SP,MyID) < ChaseSPPauseSP and GetTick() - math.max(LastMovedTime,LastSPTime) > (5000-ChaseSPPauseTime)))or distance == -1) then -- MYOWNER_OUTSIGNT_IN MyState = FOLLOW_ST TraceAI ("IDLE_ST -> FOLLOW_ST") return OnFOLLOW_ST() end if UseAutoHeal==3 then if DoHealingTasks(MyID) == 1 then return end end DoAutoBuffs(-2) if UseIdleWalk ~=0 and HPPercent(MyID) > AggroHP and SPPercent(MyID) > math.max(AggroSP,IdleWalkSP) then TraceAI("IDLE_ST -> IDLEWALK_ST, idle walk mode ="..UseIdleWalk) MyState=IDLEWALK_ST end end function OnFOLLOW_ST () TraceAI ("OnFOLLOW_ST - follow try count: "..FollowTryCount.." ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10)) local dist = GetDistanceFromOwner(MyID) if dist > GetMoveBounds() then ReturnToMoveHold = 0 StickyX,StickyY=0,0 end if ReturnToMoveHold ~=0 then dist = GetDistanceAP(MyID,StickyX,StickyY) end if (dist <= DiagonalDist(FollowStayBack+1)) then -- DESTINATION_ARRIVED_IN FollowTryCount=0 MyState = IDLE_ST TraceAI ("FOLLOW_ST -> IDLE_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10)) if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() end else if (FollowTryCount > FollowTryPanic and GetV(V_MOTION,MyID)~=MOTION_MOVE) then if FollowTryCount > 2*FollowTryPanic then if FollowTryCount > 3*FollowTryPanic then TraceAI("FOLLOW_ST -> IDLE_ST - Can't follow even using panic'ed methods - Giving up") MyState=IDLE_ST if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() end else if ReturnToMoveHold ==0 then MoveToOwner(MyID) FollowTryCount=FollowTryCount+1 TraceAI("Emergency follow - MoveToOwner() called") else TraceAI("FOLLOW_ST -> IDLE_ST - Can't get to move sticky location even using panic'ed methods - Giving up") MyState=IDLE_ST if (FastChangeCount < FastChangeLimit and FastChange_F2I==1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() end end end else FollowTryCount=FollowTryCount+1 if ReturnToMoveHold==0 then BetterMoveToOwner (MyID,1) else BetterMoveToLoc(MyID,1,StickyX,StickyY) end end else local dx,dy if ReturnToMoveHold==0 then dx,dy=BetterMoveToOwnerXY(MyID,FollowStayBack) else dx,dy=BetterMoveToLocXY(MyID,FollowStayBack,StickyX,StickyY) end local mx,my=GetV(V_POSITION,MyID) --TraceAI("followobstacle dest"..formatpos(dx,dy).." pos "..formatpos(mx,my)) if math.abs(my-dy) <=1 then --TraceAI("math.abs(my-dy) <=1") if math.abs(mx-dx) <=1 then --We could be in a bounce loop, better see if we are --TraceAI("math.abs(my-dy) <=1") if MyPosY[1]==my then for v=2,5,1 do if MyPosX[v]==dx then MoveToOwner(MyID) FollowTryCount=FollowTryCount+1 TraceAI("Bounce loop detected, emergency measures taken") break end if v==5 then MyDestX,MyDestY=dx,dy Move(MyID,MyDestX,MyDestY) end end else MyDestX,MyDestY=dx,dy Move(MyID,MyDestX,MyDestY) end else --TraceAI("math.abs(mx-dx) > 1"..math.abs(mx-dx)) MyDestX,MyDestY=dx,dy Move(MyID,MyDestX,MyDestY) end else --TraceAI("math.abs(my-dy) > 1"..math.abs(my-dy)) MyDestX,MyDestY=dx,dy Move(MyID,MyDestX,MyDestY) end if (GetV(V_MOTION,MyID) ~= MOTION_MOVE) then FollowTryCount=FollowTryCount+1 else FollowTryCount=0 end end TraceAI ("FOLLOW_ST -> FOLLOW_ST ownerpos: "..formatpos(GetV(V_POSITION,GetV(V_OWNER,MyID))).."my pos history:"..formatmypos(10).."dest cell"..formatpos(MyDestX,MyDestY)) return end end function OnCHASE_ST () MyAttackStanceX,MyAttackStanceY = 0,0 TraceAI ("OnCHASE_ST") aggro = GetAggroCount() if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then BerserkMode=1 end if DoAutoBuffs(-1) == 1 then DoAutoBuffs(2) end if (UseSkillOnly==1 and MySkill ~= 0) then skill,level=GetAtkSkill(MyID) else skill=nil level=nil end if true==IsOutOfSight(MyID,MyEnemy) then value="true " else value="false" end if(IsNotKS(MyID,MyEnemy)==0) then local reason=GetKSReason(MyID,MyEnemy) TraceAI ("CHASE_ST -> IDLE_ST : Enemy is taken "..reason) MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX, MyDestY = 0,0 ChaseGiveUpCount=0 if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() end end if (true == IsOutOfSight(MyID,MyEnemy)) then -- ENEMY_OUTSIGHT_IN MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX, MyDestY = 0,0 TraceAI ("CHASE_ST -> IDLE_ST : Enemy out of sight") ChaseGiveUpCount=0 if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() else return end end if GetV(V_MOTION,MyID)~=MOTION_MOVE then if ChaseGiveUpCount > ChaseGiveUp then Unreachable[MyEnemy]=1 if SelectEnemy(GetEnemyList(MyID,-2)) == MyEnemy then --Oh crap, TraceAI("CHASE_ST -> FOLLOW_ST : Target "..MyEnemy.." marked unreachable but is also rescue target! Trying follow state in hopes of a clean line of attack from owner") MyState = FOLLOW_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX,MyDestY=0,0 ChaseGiveUpCount=0 return OnFOLLOW_ST() else MyState = IDLE_ST MyDestX, MyDestY = 0,0 TraceAI ("CHASE_ST -> IDLE_ST : Marking target "..MyEnemy.." unreachable") ChaseGiveUpCount=0 MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() else return end end end ChaseGiveUpCount=ChaseGiveUpCount+1 elseif MyEnemies[3]==MyEnemy and GetDistanceAPR(MyEnemy,MyPosX[3],MyPosY[3]) <= GetDistanceAR(MyID,MyEnemy) then ChaseGiveUpCount=ChaseGiveUpCount+1 TraceAI("CHASE_ST: We're not getting any closer - we were "..GetDistanceAPR(MyEnemy,MyPosX[3],MyPosY[3]).." cells away 2 cycles ago, now "..GetDistanceAR(MyID,MyEnemy).." Increment ChaseGiveUpCount") end OnChaseStart() if OpportunisticTargeting ==1 and MySkill==0 and SuperPassive~=1 then if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then aggro=1 else aggro=0 end object=SelectEnemy(GetEnemyList(MyID,aggro),MyEnemy) if object ~= 0 then TraceAI("Opportunistic target change - dropping target "..MyEnemy.." for target "..object) MyEnemy=object EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} end elseif ((MySkill==MH_TINDER_BREAKER or MySkill==MH_EQC or MySkill==MH_CBC) and EleanorMode==0) or ((MySkill==MH_SONIC_CRAW or MySkill==MH_SILVERVEIN_RUSH or MySkill==MH_MIDNIGHT_FRENZY) and EleanorMode==1) then if EleanorDoNotSwitchMode == 1 then TraceAI("Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1") logappend("AAI_ERROR","Was told to use "..FormatSkill(MySkill,MyLevel).." but am in wrong mode for it, and EleanorDoNotSwitchMode==1") else if AutoSkillTimeout < GetTick() then DoSkill(MH_STYLE_CHANGE,1,MyID,8) end end end if (true == IsInAttackSight(MyID,MyEnemy,skill,level)) then -- ENEMY_INATTACKSIGHT_IN MyState = ATTACK_ST AttackTimeout=GetTick()+AttackTimeLimit ExChaseGiveUpCount=ChaseGiveUpCount ChaseGiveUpCount=0 MySkillUsedCount=0 TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN") if (FastChangeCount < FastChangeLimit and FastChange_C2A == 1) then FastChangeCount = FastChangeCount+1 return OnATTACK_ST() else return end elseif UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then dist=GetDistanceA(MyID,MyEnemy) local tact_skill,tact_debuff,tact_sp,tact_skillclass=GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy) skilltouse={-1,0,0} local SkillList=GetTargetedSkills(MyID) local availsp = GetV(V_SP,MyID) if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then availsp = availsp - tact_sp end TraceAI("Begin autoskill while chasing routine") if (tact_skill < 0) then -- Negative value of TACT_SKILL -> 1 cast of skill skill_level=tact_skill*-1 -- with level = to the absolute value of the tact_skill=1 -- value of TACT_SKILL. else skill_level=11 end for i,v in ipairs(SkillList) do skilltype=v[1] if v[2]~=0 then if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then if (skilltype == MOB_ATK and UseHomunSSkillChase==1 and AutoMobMode~=0 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then local mobskill_level=skill_level if AOEFixedLevel == 1 then mobskill_level=v[3] end local mobmode=0 if AutoMobMode==2 then mobmode=1 end mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode) --TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level))) if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then if (skilltouse[1] < 2) then skilltouse=v end end end elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then if (tact_debuff*-1 == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end end elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_S) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==S_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_OLD) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==COMBO_ATK and UseHomunSSkillChase==1 and (AutoComboMode==2 or (AutoComboMode==1 and (tact_skillclass == CLASS_COMBO_1 or tact_skillclass==CLASS_COMBO_2)) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_SILVERVEIN_RUSH or tact_skillclass~=CLASS_COMBO_1)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==GRAPPLE_ATK and UseHomunSSkillChase==1 and (AutoComboMode==2 or (AutoComboMode==1 and tact_skillclass > 5) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_TINDER_BREAKER or (v[2]==MH_CBC and tact_skillclass~=CLASS_GRAPPLE) or tact_skillclass==CLASS_GRAPPLE_2 )) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==MINION_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (tact_skillclass==CLASS_MINION or tact_skillclass==CLASS_MIN_OLD or tact_skillclass==CLASS_MIN_S)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end end end end end if skilltouse[2]~=0 then TraceAI("Using skill while chasing:"..skilltouse[2]) local slvl=skilltouse[3] if skill_level~=11 then slvl=skill_level end DoSkill(skilltouse[2],slvl,MyEnemy) if skilltouse[1] == DEBUFF_ATK then ChaseDebuffUsed=1 end MySkillUsedCount=1 end else TraceAI("Not in range, and can't use chase skill") end if (GetTact(TACT_CHASE,MyEnemy)~=1) then local alt = 0 if (ChaseGiveUpCount >= 4 and MyPosX[1] == MyPosX[3] and MyPosY[1]==MyPosY[3]) then alt=math.random(2) TraceAI("Using alt movement"..alt) end ex,ey=GetV(V_POSITION,MyEnemy) TraceAI("State History: "..MyStates[1].." "..MyStates[2].." "..MyStates[3].." "..MyStates[4].." "..MyStates[5]) TraceAI("Pos History: "..formatpos(MyPosX[1],MyPosY[1]).." "..formatpos(MyPosX[2],MyPosY[2]).." "..formatpos(MyPosX[3],MyPosY[3]).." "..formatpos(MyPosX[4],MyPosY[4]).." "..formatpos(MyPosX[5],MyPosY[5])) TraceAI("Enemy History: "..MyEnemies[1].." "..MyEnemies[2].." "..MyEnemies[3].." "..MyEnemies[4].." "..MyEnemies[5]) TraceAI("Enemy Pos History: "..formatpos(EnemyPosX[1],EnemyPosY[1]).." "..formatpos(EnemyPosX[2],EnemyPosY[2]).." "..formatpos(EnemyPosX[3],EnemyPosY[3]).." "..formatpos(EnemyPosX[4],EnemyPosY[4]).." "..formatpos(EnemyPosX[5],EnemyPosY[5])) TraceAI("current enemy: "..MyEnemy.." "..formatpos(ex,ey)) if MyStates[1]==CHASE_ST and MyStates[2]==ATTACK_ST and MyStates[3]==CHASE_ST and MyEnemy==MyEnemies[2] and IsPlayer(MyEnemy)~=1 and EnemyPosX[3]==ex and EnemyPosY[3]==ey then x,y=ex,ey Unreachable[MyEnemy]=1 TraceAI("CHASE_ST: We transitioned to attack state vs this target 2 cycles ago, now we're chasing it again, and it hasn't moved! Trying to move to it's location, and deprioritizing monster to prevent loop.") elseif (ChaseGiveUpCount > 7 or Unreachable[MyEnemy]==1) and IsPlayer(MyEnemy)~=1 then x,y=ex,ey elseif EnemyPosX[3]~=0 and EnemyPosY[3]~=0 and (ex~=EnemyPosX[3] or ey~=EnemyPosY[3]) and MyEnemy==MyEnemies[3] and alt==0 then dx,dy = ex-EnemyPosX[3],ey-EnemyPosY[3] x,y = Closest(MyID,ex+dx,ey+dy,AttackRange(MyID,MySkill,MySkillLevel),alt) else x,y = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt) end ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) if GetDistanceAPR(GetV(V_OWNER,MyID),x,y) < GetMoveBounds() then if ((x~=MyDestX or y~=MyDestY) or GetV(V_MOTION,MyID)~=MOTION_MOVE) then MyDestX, MyDestY=x,y Move (MyID,MyDestX,MyDestY) TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN "..MyDestX..","..MyDestY.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y)) else TraceAI("CHASE_ST -> CHASE_ST : Destination not changed "..MyDestX..","..MyDestY.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y)) end else --if ChaseGiveUpCount > 4 then MyState = IDLE_ST Unreachable[MyEnemy]=1 MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX, MyDestY = 0,0 TraceAI ("CHASE_ST -> IDLE_ST : Following enemy would exceed move bounds."..x..","..y.."mypos "..MyPosX[1]..","..MyPosY[1].."owner pos"..ox..","..oy.." enemypos "..ex..","..ey.." GetDistanceAPR="..GetDistanceAPR(GetV(V_OWNER,MyID),x,y)) ChaseGiveUpCount=0 if (FastChangeCount < FastChangeLimit and FastChange_C2I == 1) then FastChangeCount = FastChangeCount+1 return OnIDLE_ST() end end end return end function OnATTACK_ST () TraceAI ("OnATTACK_ST") if (true == IsOutOfSight(MyID,MyEnemy)) then -- first thing's first, if enemy is gone drop it. MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkillUseCount= 0 TraceAI ("ATTACK_ST -> IDLE_ST -- target gone") return OnIDLE_ST() end if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then -- Enemy dead? Okay we're done here - drop it. MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkillUseCount= 0 TraceAI ("ATTACK_ST -> IDLE_ST Enemy dead") return OnIDLE_ST() end if (AttackTimeout < GetTick() and AttackTimeLimit > 0) then -- Attack time limit reached. MyState = FOLLOW_ST Unreachable[MyEnemy]=1 MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkillUseCount= 0 TraceAI ("ATTACK_ST -> FOLLOW_ST -- attack timeout reached, so we're probably posbugged. Dropping target and returning to owner in the hope that that sorts it out") return OnFOLLOW_ST() end local aggro = GetAggroCount() if(aggro > UseBerserkMobbed and UseBerserkMobbed > 0)then BerserkMode=1 end DoAutoBuffs(2) local skill,level if UseSkillOnly==1 then skill,level=GetAtkSkill(MyID) elseif MySkill~=0 then skill,level=MySkill,MySkillLevel else skill,level=nil,nil end if (false == IsInAttackSight(MyID,MyEnemy,skill,level)) then -- Check if we can attack enemy, if not back to chase ResetCounters() MyState=CHASE_ST TraceAI ("ATTACK_ST -> CHASE_ST : ENEMY_OUTATTACKSIGHT_IN MyEnemy: "..MyEnemy.." distance to "..GetDistanceA(MyID,MyEnemy)) if (FastChangeCount < FastChangeLimit and FastChange_A2C == 1) then FastChangeCount = FastChangeCount+1 return OnCHASE_ST() end end --### START SECTION TO DETECT OBSTACLES AND POSBUG ###--- if (GetV(V_TARGET,MyID)~=MyEnemy and MyStates[1]~=ATTACK_ST) then--and GetV(V_TARGET,MyID)~=0) then -- troubles getting to target for first attack, assume obstacles. AttackGiveUpCount=AttackGiveUpCount+1 if (AttackGiveUpCount >= 3 and AttackGiveUpCount <= AttackGiveUp) then MyDestX, MyDestY = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt)--ClosestR (MyID,MyEnemy,math.max(AttackRange(MyID,MySkill,MySkillLevel)-1,1),math.floor((AttackGiveUpCount-1)/2)) Move (MyID,MyDestX,MyDestY) elseif (AttackGiveUpCount > AttackGiveUp) then Unreachable[MyEnemy]=1 MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MySkillUseCount= 0 TraceAI ("ATTACK_ST -> IDLE_ST - Can't target enemy, marked as unreachable") return OnIDLE_ST() end else AttackGiveUpCount=0 attackok=1 end --### END SECTION TO DETECT OBSTACLES AND POSBUG ###--- if (MyAttackStanceX==0) then x,y=GetV(V_POSITION,MyID) MyAttackStanceX,MyAttackStanceY=x,y logappend("AAI_DANCE","Attack Stance set to "..MyDestX..","..MyDestY.." current pos: "..x..","..y) end if (UseAutoPushback > 0) then if DoAutoPushback(MyID) == nil then return end end OnAttackStart() local tact_skill,tact_debuff,tact_sp,tact_skillclass=GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy) local skill_level --logappend("AAI_ATK","tact_skill set "..tact_skill) --if AutoMobMode==1 then -- mskill,mlevel=GetMobSkill(MyID) -- mobcount=GetMobCount(mskill,mlevel,MyEnemy,1) -- --TraceAI("mskill/level "..FormatSkill(mskill,mlevel).." mobcount "..mobcount) --elseif AutoMobMode==2 then -- mskill,mlevel=GetMobSkill(MyID) -- mobcount=GetMobCount(mskill,mlevel,MyEnemy,0) --else -- mobcount=0 --end --Sniping routine if (IsHomun(MyID)==1 and SuperPassive~=1 and BerserkMode==0 and (GetTick() >= AutoSkillTimeout) and aggro <= AutoMobCount and GetTact(TACT_SNIPE,MyEnemy)==SNIPE_OK and (ShouldStandby == 0 or StickyStandby ==0)) then target=SelectEnemy(GetEnemyList(MyID,2)) -- This actually checks range - I know it's ugly to do it there, skill range checks need to be done at that point so we can pick a low priority target thats in range, instead of a high priority one out of range. if target ~=0 then snipeskill=0 local snipe_tact_skillclass=GetTact(TACT_SKILLCLASS,target) if snipe_tact_skillclass == CLASS_MINION then snipeskill,snipelevel=GetMinionSkill(MyID) end if snipeskill==0 and (snipe_tact_skillclass == CLASS_S or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_S) then snipeskill,snipelevel=GetSAtkSkill(MyID) end if snipeskill==0 and (snipe_tact_skillclass == CLASS_OLD or snipe_tact_skillclass == CLASS_BOTH or snipe_tact_skillclass == CLASS_MIN_OLD) then snipeskill,snipelevel=GetAtkSkill(MyID) end --TraceAI("snipe "..skill.." level"..level) if snipeskill ~=0 then slevel = GetTact(TACT_SKILL,target) if slevel < 0 then slevel=-1*slevel if slevel > snipelevel then slevel = snipelevel end if ((GetV(V_SP,MyID)-ReserveSP >= GetTact(TACT_SP,target)+GetSkillInfo(snipeskill,3,slevel))) then TraceAI("Snipe attack on "..target.." "..snipeskill.." "..slevel) DoSkill(snipeskill,slevel,target) end end end end end -- Begin skill selection routine skilltouse = {-1,0,0} -- First digit (1): -1 = no skill, 0 single target, 1 debuff, 2 mob -- Second digit (2): skill id -- Third digit (3): skill level if (1==1) then --non paniced attack if (MySkill==0 and UseAttackSkill == 1 and GetTick() >= AutoSkillTimeout) then if (tact_skill < 0) then -- Negative value of TACT_SKILL -> 1 cast of skill skill_level=tact_skill*-1 -- with level = to the absolute value of the tact_skill=1 -- value of TACT_SKILL. else skill_level=11 end local SkillList=GetTargetedSkills(MyID) TraceAI("Begin autoskill routine") local availsp = GetV(V_SP,MyID) if BerserkMode~=1 or Berserk_IgnoreMinSP ~=1 then availsp = availsp - tact_sp end for i,v in ipairs(SkillList) do skilltype=v[1] TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass"..tact_skillclass.."v"..v[1].." "..v[2].." "..v[3]) if v[2]~=0 then if IsInAttackSight(MyID,MyEnemy,v[2],v[3])==true then if (skilltype == MOB_ATK and UseHomunSSkillAttack==1 and AutoMobMode~=0 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then local mobskill_level=skill_level if AOEFixedLevel == 1 then mobskill_level=v[3] end local mobmode=0 if AutoMobMode==2 then mobmode=1 end mobskillcount=GetMobCount(v[2],math.min(v[3],mobskill_level),MyEnemy,mobmode) --TraceAI("mobskillcount="..mobskillcount.."tact_skillclass="..tact_skillclass.."class_mob="..CLASS_MOB.."AutoMobCount="..AutoMobCount.." "..FormatSkill(v[2],math.min(v[3],mobskill_level))) if (mobskillcount >= AutoMobCount or tact_skillclass == CLASS_MOB) then if (availsp >= GetSkillInfo(v[2],3,math.min(v[3],mobskill_level)))then if (skilltouse[1] < 2) then skilltouse=v end end end elseif (skilltype ==DEBUFF_ATK and AttackDebuffUsed < AttackDebuffLimit) then if (tact_debuff*-1 == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end end elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_S) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==S_ATK and UseHomunSSkillAttack==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD and tact_skillclass~=CLASS_MINION and tact_skillclass~=CLASS_MIN_OLD) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==COMBO_ATK and UseHomunSSkillAttack==1 and (AutoComboMode==2 or (AutoComboMode==1 and (tact_skillclass == CLASS_COMBO_1 or tact_skillclass==CLASS_COMBO_2)) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_SILVERVEIN_RUSH or tact_skillclass~=CLASS_COMBO_1)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==GRAPPLE_ATK and UseHomunSSkillAttack==1 and (AutoComboMode==2 or (AutoComboMode==1 and tact_skillclass > 5) or Berserk_ComboAlways==1) and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (v[2] == MH_TINDER_BREAKER or (v[2]==MH_CBC and tact_skillclass~=CLASS_GRAPPLE) or tact_skillclass==CLASS_GRAPPLE_2 )) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end elseif (skilltype==MINION_ATK and UseHomunSSkillAttack==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and (tact_skillclass==CLASS_MINION or tact_skillclass==CLASS_MIN_OLD or tact_skillclass==CLASS_MIN_S)) then if (availsp-ReserveSP >= GetSkillInfo(v[2],3,math.min(v[3],skill_level))) then skilltouse=v end end end end TraceAI("skill selected "..skilltouse[2]) end end -- Now we finalize the selection if skilltouse[1]~= -1 then MySkill=skilltouse[2] if (IsHomun(MyID)==1 and skill_level~=11 and (skilltouse[1]~=MOB_ATK or AOEFixedLevel ~= 1)) then --no need to check what skill MySkillLevel=skill_level --Only homuns can use non-max level else --and they dont have any mob/debuffs MySkillLevel=skilltouse[3] end if (skilltouse[1] == DEBUFF_ATK) then AttackDebuffUsed=AttackDebuffUsed+1 else MySkillUsedCount=MySkillUsedCount+1 end end end -- Now we resolve it --if (MySkill == 0) then if (UseSkillOnly ~= 1) then Attack (MyID,MyEnemy) TraceAI("Normal attack vs: "..MyEnemy) if GetV(V_HOMUNTYPE,MyID) == ELEANOR then MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0) UpdateTimeoutFile() end end -- else if (MySkill ~=0) then TraceAI("Skill Attack: "..MySkill.." target: "..MyEnemy.." level:"..MySkillLevel) SkillTarget=MyEnemy if (MySkill==MA_SHARPSHOOTING and AoEMaximizeTargets==1) then target,hitcount=GetBestFASTarget(MyID) TraceAI(target.." - "..hitcount) if hitcount > 0 and target~=-1 then SkillTarget=target elseif hitcount== 0 then MySkill=0 end TraceAI("Skill Attack: "..MySkill.." (FAS) target: "..SkillTarget.." enemy: "..MyEnemy) elseif (MySkill==ML_BRANDISH and AoEMaximizeTargets==1) then target=GetBestBrandishTarget(MyID) if target~=-1 then SkillTarget=target end TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy) elseif (MySkill==MH_HAILAGE_STAR or MySkill==MS_BOWLING_BASH) then target=GetBestAoETarget(MyID,MySkill,MySkillLevel) if target~=-1 then SkillTarget=target end TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy) elseif (MySkill==MH_XENO_SLASHER or MySkill==MH_LAVA_SLIDE or MySkill==MA_SHOWER or MySkill==MH_POISON_MIST or MySkill==MH_VOLCANIC_ASH) then targx,targy=GetBestAoECoord(MyID,MySkill,MySkillLevel) if targx~=-1 then SkillTargetX,SkillTargetY=targx,targy end TraceAI("Skill Attack: "..MySkill.." (brandish) target: "..SkillTarget.." enemy: "..MyEnemy) end if MySkill ~=0 then if GetV(V_HOMUNTYPE,MyID) == ELEANOR then MySpheres = math.max(math.min(10,MySpheres+1/SphereTrackFactor),0) UpdateTimeoutFile() end DoSkill(MySkill,MySkillLevel,SkillTarget,-1,SkillTargetX,SkillTargetY) end end if ((UseSkillOnly ~= 1 and UseDanceAttack==1 and GetV(V_SP,MyID) >= DanceMinSP) or (BerserkMode==1 and Berserk_Dance==1) or (panicmode==1 and Panic_UseDanceAttack==1 and HPPercent(MyID) > FleeHP)) and (IsHomun(MyID)==1 and MySkill==0) and GetDistanceRect(MyEnemy,GetV(V_OWNER,MyID)) < 13 then nx,ny=GetDanceCell(MyAttackStanceX,MyAttackStanceY,MyEnemy) if GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny) >= GetMoveBounds() then logappend("AAI_DANCE","Dance attack canceled, too close to move bounds "..GetDistanceAPR(GetV(V_OWNER,MyID),nx,ny).." "..GetMoveBounds()) else logappend("AAI_DANCE","Dancing between "..MyAttackStanceX..","..MyAttackStanceY.." and "..nx..","..ny) Move(MyID,nx,ny) Attack(MyID,MyEnemy) Move(MyID,MyAttackStanceX,MyAttackStanceY) end end MySkill = 0 MySkillLevel=0 end ------------------- -- TANK ROUTINES -- ------------------- function OnTANKCHASE_ST () if (UseSkillOnly==1) then skill,level=GetAtkSkill(MyID) elseif (UseSkillOnly==-1) then skill,level=GetAtkSkill(MyID) if (GetV(V_SP,MyID)-GetTact(TACT_SP,MyEnemy) < GetSkillInfo(skill,3,level)) then skill,level,sp=nil,nil,nil end else skill,level,sp=nil,nil,nil end TraceAI ("OnTANKCHASE_ST") if (true == IsOutOfSight(MyID,MyEnemy) or (ChaseGiveUpCount > ChaseGiveUp and GetV(V_MOTION,MyID)~=MOTION_MOVE)) then -- ENEMY_OUTSIGHT_IN if (ChaseGiveUpCount>ChaseGiveUp) then Unreachable[MyEnemy]=1 end MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX, MyDestY = 0,0 TraceAI ("TANKCHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN") ChaseGiveUpCount=0 return end if (true == IsInAttackSight(MyID,MyEnemy)) then -- ENEMY_INATTACKSIGHT_IN ChaseGiveUpCount=0 if(IsNotKS(MyID,MyEnemy)==1) then MyState = TANK_ST MySkillUsedCount=0 TraceAI ("TANKCHASE_ST -> TANK_ST : ENEMY_INATTACKSIGHT_IN") return OnTANK_ST() else MyState = IDLE_ST MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} MyDestX, MyDestY = 0,0 TraceAI ("TANKCHASE_ST -> IDLE_ST : Enemy is taken") end return end TraceAI("Tank chase: Can we skill while chasing?") if UseSkillOnly == -1 and (GetTick() >= AutoSkillTimeout) then dist=GetDistanceA(MyID,MyEnemy) tact_debuff,tact_skill,tact_sp,tact_skillclass=GetTact(TACT_DEBUFF,MyEnemy),GetTact(TACT_SKILL,MyEnemy),GetTact(TACT_SP,MyEnemy),GetTact(TACT_SKILLCLASS,MyEnemy) skilltouse={-1,0,0} local SkillList=GetTargetedSkills(MyID) TraceAI("Begin autoskill while tank chasing routine") if (tact_skill < 0) then -- Negative value of TACT_SKILL -> 1 cast of skill skill_level=tact_skill*-1 -- with level = to the absolute value of the tact_skill=1 -- value of TACT_SKILL. else skill_level=11 end for i,v in ipairs(SkillList) do skilltype=v[1] if v[2]~=0 then --logappend("AAI_Chase","skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.." tact_skillclass ".. tact_skillclass.."v"..v[1].." "..v[2].." "..v[3]) if (GetV(V_SP,MyID) >= tact_sp+GetSkillInfo(v[2],3,math.min(v[3],skill_level)) and GetSkillInfo(v[2],2,math.min(v[3],skill_level)) >= dist) then --TraceAI("skilltype ".. skilltype.." MySkillUsedCount "..MySkillUsedCount.." tact_skill ".. tact_skill.."v"..v[1].." "..v[2].." "..v[3]) if (skilltype == MOB_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1))) then if (tact_skillclass == CLASS_MOB) then if (skilltouse[1] < 2) then skilltouse=v end end elseif (skilltype ==DEBUFF_ATK and ChaseDebuffUsed==0) then if (tact_debuff*-1 == v[2] or (tact_debuff==1 and BasicDebuffs[v[2]]~=nil)) then skilltouse=v end elseif (skilltype==MAIN_ATK and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_S) then skilltouse=v elseif (skilltype==S_ATK and UseHomunSSkillChase==1 and (MySkillUsedCount < tact_skill or tact_skill==SKILL_ALWAYS or (BerserkMode==1 and Berserk_SkillAlways==1)) and tact_skillclass~=CLASS_OLD) then skilltouse=v end end end end if skilltouse[2]~=0 then TraceAI("Using skill while tank chasing:"..skilltouse[2]) local slvl=skilltouse[3] if skill_level~=11 then slvl=skill_level end DoSkill(skilltouse[2],slvl,MyEnemy,-1) if skilltouse[1] == DEBUFF_ATK then ChaseDebuffUsed=1 end MySkillUsedCount=1 end else TraceAI("Not in range, and can't use chase skill") end ChaseGiveUpCount=ChaseGiveUpCount+1 MyDestX, MyDestY = GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt) if (GetTact(TACT_CHASE,MyEnemy)~=1) then Move (MyID,MyDestX,MyDestY) TraceAI ("TANKCHASE_ST -> TANKCHASE_ST : DESTCHANGED_IN"..MyDestX.." "..MyDestY) end return end function OnTANK_ST() if (GetV(V_MOTION,MyEnemy)==MOTION_DEAD or IsOutOfSight(MyID,MyEnemy)) then MyState=IDLE_ST TraceAI("TANK_ST->IDLE_ST - Target dead or out of sight") return end if (GetV(V_TARGET,MyEnemy)~=MyID and (TankHitTimeout + 1500) < GetTick()) then TankHitTimeout = GetTick() if (IsInAttackSight(MyID,MyEnemy)==true) then Attack(MyID,MyEnemy) else MyState=TANKCHASE_ST TraceAI("TANK_ST->TANKCHASE_ST - Target out of range") end return end if GetV(V_TARGET,MyEnemy)==MyID then TraceAI("TANK_ST->IDLE_ST - Target is tanked successfully") MyState=IDLE_ST end end -------------------- --- REST ROUTINE --- -------------------- function OnREST_ST () TraceAI("OnREST_ST") if (DoIdleTasks()==nil) then return end if SuperPassive~=1 then local object = SelectEnemy(GetFriendTargets()) if (object ~= 0) then --Check for monsters attacking owner MyState = CHASE_ST MyEnemy = object TraceAI ("REST_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") return end object = SelectEnemy(GetEnemyList(MyID,0)) if (object ~= 0) then MyState = CHASE_ST MyEnemy = object TraceAI ("REST_ST -> CHASE_ST : ATTACKED_IN") return end end --If theres nothing else to do, return to the "rest station" x,y=GetV(V_POSITION,MyID) ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) xoff=x-ox yoff=y-oy TraceAI(xoff.." "..ox.." "..x) if (GetV(V_MOTION,GetV(V_OWNER,MyID))~=MOTION_SIT) then MyState=IDLE_ST TraceAI("REST_ST -> IDLE_ST: Owner stood up") elseif (xoff~=RestXOff or yoff~=RestYOff) then MyDestX=ox+RestXOff MyDestY=oy+RestYOff TraceAI("REST_ST -> REST_ST: moving from "..formatpos(x,y).." to "..formatpos(MyDestX,MyDestY)) Move(MyID,MyDestX,MyDestY) else TraceAI("REST_ST -> REST_ST: At rest station") end end ----------------------------- ---Friend motion routines --- ----------------------------- function OnFRIEND_CIRCLE_ST() x,y = GetV(V_POSITION,MyID) dist = GetDistance(x,y,NewFriendX,NewFriendY) TraceAI("OnFRIEND_CIRCLE_ST"..x..","..y.." "..dist) if FriendCircleIter == 0 then if dist > 2 then Move(MyID,NewFriendX,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout > 16 then MyState=IDLE_ST TraceAI("Dropping circle attempt, timeout exceeded") end return else FriendCircleIter=1 end end if FriendCircleIter ==1 then if x==NewFriendX and y==NewFriendY-2 then FriendCircleIter=2 FriendCircleTimeout=0 else Move(MyID,NewFriendX,NewFriendY-2) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=2 FriendCircleTimeout=0 TraceAI("giving up on circle step 1") end return end end if FriendCircleIter ==2 then if x==NewFriendX-2 and y==NewFriendY then FriendCircleIter=3 FriendCircleTimeout=0 else Move(MyID,NewFriendX-2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=3 FriendCircleTimeout=0 TraceAI("giving up on circle step 2") end return end end if FriendCircleIter ==3 then if x==NewFriendX and y==NewFriendY+2 then FriendCircleIter=4 FriendCircleTimeout=0 else Move(MyID,NewFriendX,NewFriendY+2) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=4 FriendCircleTimeout=0 TraceAI("giving up on circle step 3") end return end end if FriendCircleIter ==4 then if x==NewFriendX+2 and y==NewFriendY then FriendCircleIter=5 FriendCircleTimeout=0 else Move(MyID,NewFriendX+2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=5 FriendCircleTimeout=0 TraceAI("giving up on circle step 4") end return end end if FriendCircleIter == 5 then if x==NewFriendX and y==NewFriendY-2 then FriendCircleIter=0 FriendCircleTimeout=0 MyState=IDLE_ST TraceAI("Circle completed") return else Move(MyID,NewFriendX,NewFriendY-2) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=0 FriendCircleTimeout=0 MyState=IDLE_ST TraceAI("giving up on circle step 5") end return end end end function OnFRIEND_CROSS_ST() x,y = GetV(V_POSITION,MyID) dist = GetDistance(x,y,NewFriendX,NewFriendY) TraceAI("OnFRIEND_CROSS_ST"..x..","..y.." "..dist) if FriendCircleIter == 0 then if dist > 2 then Move(MyID,NewFriendX,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout > 16 then MyState=IDLE_ST TraceAI("Dropping cross attempt, timeout exceeded") end return else FriendCircleIter=1 end end if FriendCircleIter ==1 then if x==NewFriendX-2 and y==NewFriendY then FriendCircleIter=2 FriendCircleTimeout=0 else Move(MyID,NewFriendX-2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=2 FriendCircleTimeout=0 TraceAI("giving up on cross step 1") end return end end if FriendCircleIter ==2 then if x==NewFriendX+2 and y==NewFriendY then FriendCircleIter=3 FriendCircleTimeout=0 else Move(MyID,NewFriendX+2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=3 FriendCircleTimeout=0 TraceAI("giving up on cross step 2") end return end end if FriendCircleIter ==3 then if x==NewFriendX-2 and y==NewFriendY then FriendCircleIter=4 FriendCircleTimeout=0 else Move(MyID,NewFriendX-2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=4 FriendCircleTimeout=0 TraceAI("giving up on cross step 3") end return end end if FriendCircleIter ==4 then if x==NewFriendX+2 and y==NewFriendY then FriendCircleIter=5 FriendCircleTimeout=0 else Move(MyID,NewFriendX+2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=5 FriendCircleTimeout=0 TraceAI("giving up on cross step 4") end return end end if FriendCircleIter == 5 then if x==NewFriendX-2 and y==NewFriendY then FriendCircleIter=0 FriendCircleTimeout=0 MyState=IDLE_ST TraceAI("Circle completed") return else Move(MyID,NewFriendX-2,NewFriendY) FriendCircleTimeout=FriendCircleTimeout+1 if FriendCircleTimeout>4 then FriendCircleIter=0 FriendCircleTimeout=0 MyState=IDLE_ST TraceAI("giving up on cross step 5") end return end end end ------------------- --Command State Process ------------------- function OnMOVE_CMD_ST () TraceAI ("OnMOVE_CMD_ST") local x, y = GetV (V_POSITION,MyID) if (x == MyMoveX and y == MyMoveY) then StickyX,StickyY=0,0 if (MoveSticky ~= 0) then if (ReturnToMoveHold==0) then ReturnToMoveHold = 1 StickyX,StickyY=x,y else ReturnToMoveHold = 0 end end MyState=IDLE_ST return OnIDLE_ST() elseif GetV(V_MOTION,MyID) == MOTION_STAND or MyDestX~=MyMoveX or MyDestY~=MyMoveY then MyDestX,MyDestY=MyMoveX,MyMoveY Move(MyID,MyDestX,MyDestY) end end function OnMOVE_CMD_HOLD_ST () TraceAI ("OnMOVE_CMD_HOLD_ST") MySkillUsedCount = 0 ChaseGiveUpCount = 0 AttackGiveUpCount = 0 ChaseDebuffUsed = 0 AttackDebuffUsed = 0 BypassKSProtect = 0 BerserkMode = 0 if (DoIdleTasks()==nil) then return end if (MoveStickyFight==1 and SuperPassive~=1 ) then local object = SelectEnemy(GetFriendTargets()) if (object ~= 0) then -- MYOWNER_ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end object = SelectEnemy(GetEnemyList(MyID,0)) if (object ~= 0) then -- ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("MOVE_CMD_HOLD_ST -> CHASE_ST : ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end object = SelectEnemy(GetEnemyList(MyID,-1)) if (object ~= 0) then MyState = TANKCHASE_ST MyEnemy = object TraceAI ("MOVE_CMD_HOLD_ST -> TANKCHASE_ST") return end end end function OnSTOP_CMD_ST () end function OnATTACK_OBJECT_CMD_ST () end function OnATTACK_AREA_CMD_ST () TraceAI ("OnATTACK_AREA_CMD_ST") local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) end if (object ~= 0) then -- MYOWNER_ATTACKED_IN or ATTACKED_IN MyState = CHASE_ST MyEnemy = object return end local x , y = GetV (V_POSITION,MyID) if (x == MyDestX and y == MyDestY) then -- DESTARRIVED_IN MyState = IDLE_ST end end function OnPATROL_CMD_ST () TraceAI ("OnPATROL_CMD_ST") local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) end if (object ~= 0) then -- MYOWNER_ATTACKED_IN or ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("PATROL_CMD_ST -> CHASE_ST : ATTACKED_IN") return end local x , y = GetV (V_POSITION,MyID) if (x == MyDestX and y == MyDestY) then -- DESTARRIVED_IN MyDestX = MyPatrolX MyDestY = MyPatrolY MyPatrolX = x MyPatrolY = y Move (MyID,MyDestX,MyDestY) end end ----------------------------------------------------------------------- --IF ANYONE READING MY CODE HAS A FUCK'S CLUE WHAT THE HOLD COMMAND IS --OR WHAT IN THE DEVIL IT'S PURPOSE IS, PLEASE ENLIGHTEN ME! -Azzy ---------------------------------------------------------------------- function OnHOLD_CMD_ST () TraceAI ("OnHOLD_CMD_ST") logappend("AAI_ERROR","We're in HOLD_CMD_ST - where did hold cmd come from?") if (MyEnemy ~= 0) then local d = GetDistance(MyEnemy,MyID) if (d ~= -1 and d <= GetV(V_ATTACKRANGE,MyID)) then Attack (MyID,MyEnemy) else MyEnemy = 0 EnemyPosX = {0,0,0,0,0,0,0,0,0,0} EnemyPosY = {0,0,0,0,0,0,0,0,0,0} end return end local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) if (object == 0) then return end end MyEnemy = object end function OnSKILL_OBJECT_CMD_ST () if IsInAttackSight(MyID,MyEnemy,MySkill,MySkillLevel) then DoSkill(MySkill,MySkillLevel,MyEnemy) TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - skill used") MyState=IDLE_ST MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0 return elseif IsOutOfSight(MyID,MyEnemy) then TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - target off screen") MyState=IDLE_ST MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0 elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then TraceAI("SKILL_OBJECT_CMD_ST --> IDLE_ST - Couldn't get into range to use skill "..MySkill.." on "..MyEnemy) MyState=IDLE_ST MyDestX,MyDestY,MyEnemy,MySkill,MySkillLevel=0,0,0,0,0 return OnIDLE_ST() else x,y= GetStandPoint(MyID,MyEnemy,MySkill,MySkillLevel,alt) if x < 10 and y < 10 then local ex,ey=GetV(V_POSITION,MyEnemy) local hx,hy=GetV(V_POSITION,MyID) local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) logappend("AAI_ERROR","Anomalous move in progress: h "..formatpos(hx,hy).." "..formatmotion(GetV(V_MOTION,MyID)).." e "..formatpos(ex,ey).." "..formatmotion(GetV(V_MOTION,MyEnemy)).." o "..formatpos(ox,oy).." "..formatmotion(GetV(V_MOTION,GetV(V_OWNER,MyID))).." dest "..formatpos(x,y)) end Move(MyID,x,y) SkillObjectCMDTimeout=SkillObjectCMDTimeout+1 return end end function OnSKILL_AREA_CMD_ST () TraceAI ("OnSKILL_AREA_CMD_ST") local x , y = GetV (V_POSITION,MyID) if (GetDistance(x,y,MyDestX,MyDestY) <= AttackRange(MyID,MySkill,MySkillLevel)) then -- DESTARRIVED_IN DoSkill(MySkill,MySkillLevel,0,nil,MyDestX,MyDestY) MyState = IDLE_ST MySkill = 0 else targetx,targety=Closest(MyID,x,y,AttackRange(MyID,MySkill,MySkillLevel)) if GetDistanceAPR(GetV(V_OWNER,MyID),targetx,targety) > GetMoveBounds() then MyState = IDLE_ST MySkill = 0 TraceAI("OnSKILL_AREA_CMD_ST -> IDLE_ST Target out of range") else Move (MyID,targetx,targety) end end end function OnFOLLOW_CMD_ST () TraceAI ("OnFOLLOW_CMD_ST") local d = GetDistanceA (GetV(V_OWNER,MyID),MyID) if ( d > FollowStayBack) then BetterMoveToOwner (MyID,FollowStayBack) return end -- Start the friending process if (StandbyFriending == 1) then local actors = GetActors() for i,v in ipairs(actors) do if (IsMonster(v)~=1 and GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT) then TraceAI("Friend list modification") if (IsToRight(GetV(V_OWNER,MyID),v)==1) then if (MyFriends[v]==nil) then MyFriends[v] = 1 UpdateFriends() MyState=FRIEND_CIRCLE_ST ReturnToState=FOLLOW_CMD_ST NewFriendX,NewFriendY=GetV(V_POSITION,v) end elseif (IsToRight(v,GetV(V_OWNER,MyID))==1) then if (MyFriends[v]~=nil) then MyFriends[v] = nil UpdateFriends() MyState=FRIEND_CROSS_ST ReturnToState=FOLLOW_CMD_ST NewFriendX,NewFriendY=GetV(V_POSITION,v) end end end end end -- Okay, that's done. if (DefendStandby == 1 and SuperPassive~=1) then local object = SelectEnemy(GetFriendTargets()) if (object ~= 0) then -- MYOWNER_ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end object = SelectEnemy(GetEnemyList(MyID,0)) if (object ~= 0) then -- ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("FOLLOW_CMD_ST -> CHASE_ST : ATTACKED_IN ") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end end end --##################################### --### Targeting Routines start here ### --##################################### --[[ targets[actorid]= MotionClass -1 = Dead 0 = Stand 1 = Moving 2 = Flinch 3 = Attacking 4 = Skilling 5 = Casting 6 = Other TargetClass -2 = Non-friend player -1 = Monster 0 = None 1 = Self 2 = Friend/Owner ]]--]] function GetFriendTargets() -- returns list of targets of friends who are attacking local targets = {} for i,v in ipairs (GetActors()) do if (IsFriend(v) == 1) then motion=GetV(V_MOTION,v) target=GetV(V_TARGET,v) if (IsMonster(target)==1) then tact=GetTact(TACT_BASIC,target) if (FriendAttack[motion]==1 and tact > 0) then targets[target]={MotionClassLU[GetV(V_MOTION,target)],GetTargetClass(target),GetTact(TACT_BASIC,target),GetTact(TACT_CAST,target)} end end end end return targets end -- aggro arguments -- 2 = snipe -- 1 = aggro -- 0 = nonaggro -- -1 = tank -- -2 = rescue function GetEnemyList (myid,aggro) local owner = GetV(V_OWNER,myid) local enemys = {} if aggro==2 then sskill,slevel=GetSAtkSkill(MyID) oskill,olevel=GetAtkSkill(MyID) mskill,mlevel=GetMinionSkill(MyID) end TraceAI("GetEnemyList with aggro "..aggro) for k,v in pairs(Targets) do tact = GetTact(TACT_BASIC,k) casttact=GetTact(TACT_CAST,k) if tact==TACT_TANKMOB then if GetAggroCount() > AutoMobCount then tact = TACT_ATTACK_M else tact = TACT_TANK end end --TraceAI("Target"..k.." tact:"..tact.." Motion"..v[1].." TClass"..v[2]) if (0 < tact and (tact < 5 or tact >9) and aggro==1 and (DoNotAttackMoving ~=1 or v[1]~=1) and (tact ~= 14 or AttackLastFullSP==0 or GetSPPercent(MyID)==1)) or (tact > 9 and tact < 13 and aggro == 2 and k~=MyEnemy) or (v[2]>0 and tact>0 and (v[1]==3 or casttact >= CAST_REACT) and aggro~=2 and (aggro > -1 or (aggro==-2 and IsRescueTarget(k)==1))) or (tact == -1 and aggro==-1 and v[2]~=1) then --TraceAI("Tactics say to attack:"..k) if (IsNotKS(myid,k)==1 and v[1] > -1) then --TraceAI("Is alive and not a KS") if aggro == 0 or (aggro~=2 and v[2]>0) then if (GetMoveBounds() >= GetDistanceRect(owner,k)) then --TraceAI("Adding to target list: "..k) r={v[1],v[2],tact,casttact} enemys[k] = r --else -- tempx,tempy=GetV(V_POSITION,owner) -- targx,targy=GetV(V_POSITION,k) -- TraceAI("target ignored "..k.." mypos "..tempx..","..tempy.." enemy "..targx..","..targy.." bounds "..GetMoveBounds().."dist "..GetDistanceRect(owner,k)) end elseif aggro~=2 then if (GetAggroDist() >= GetDistanceA(owner,k)) then TraceAI("Adding to target list: "..k) r={v[1],v[2],tact,casttact} enemys[k] = r end else -- Sniping - so we need to check range on skill, instead of aggrodist dist = GetDistanceA(MyID,k) tact_skill_class=(GetTact(TACT_SKILLCLASS,k)) if tact_skill_class==CLASS_MINION and mskill~=0 then if dist <= GetSkillInfo(mskill,2,mlevel) then r={v[1],v[2],tact,casttact} enemys[k] = r end elseif sskill~=0 and tact_skill_class~=CLASS_OLD then if dist <= GetSkillInfo(sskill,2,slevel) then r={v[1],v[2],tact,casttact} enemys[k] = r end elseif oskill~=0 and tact_skill_class~=CLASS_S then if dist <= GetSkillInfo(oskill,2,olevel) then r={v[1],v[2],tact,casttact} enemys[k] = r end end end end end end return enemys end -- format of return -- enemys[n][1] = Motion Class -- enemys[n][2] = Target Class -- enemys[n][3] = tact -- enemys[n][4] = casttact function SelectEnemy(enemys,curenemy) local min_priority=-1 local priority local min_dis = 100 local dis --local min_aggro = -1 --local aggro = 0 local result=0 local max_reachable=1 --local min_mobcount=1 --local mobcount=0 if curenemy~=nil then -- it's an opportunistic attack local dist = GetDistanceA(MyID,curenemy) local aggrotemp=0 if IsFriendOrSelf(GetV(V_TARGET,curenemy)) ==1 then aggrotemp=1 end min_priority=convpriority(GetTact(TACT_BASIC,curenemy),aggrotemp) if dist < 3 then return 0 else min_dis = dist - 3 end end for k,v in pairs(enemys) do local basepriority = v[3] -- basic tact if v[2]>0 and (v[1]==3 or v[4]>=CAST_REACT) then aggro=1 else aggro=0 end priority=convpriority(basepriority,aggro) --TraceAI(k.." "..basepriority.." "..priority) --elseif ((priority==2 or priority==5) and (v[1]==3 or v[4]>=CAST_REACT)) then -- aggro=-1 --elseif then --aggro = 1 --else -- aggro=0 --end dis = GetDistanceA (MyID,k) unreachable=Unreachable[k] if unreachable == nil then unreachable=0 end --TraceAI(priority.."/"..min_priority.." "..dis.."/"..min_dis.." "..unreachable.."/"..max_reachable) if (unreachable <= max_reachable) then --if (aggro >= min_aggro) then if (priority > min_priority or (priority==min_priority and dis < min_dis)) then --if (dis < min_dis) then result = k min_dis = dis min_priority=priority --min_aggro=aggro max_reachable=unreachable --end end --end end end --TraceAI("SelectEnemy returning target "..result) return result end function convpriority(base,agr) local priority if base > 9 and base < 13 then --Snipe modes are to be treated as attack base = base-8 end if base == 13 then if agr == 1 then base = 7 else base = 2 end end if base == 14 then base= 1 end if base>6 and agr==1 then priority=base elseif base==4 or base==3 or base==15 then priority=base+1 if agr==0 then priority=priority-2 end elseif (priority==5 and aggro==1) then return 2 elseif base==2 then return 1 else return 0 end return priority end --#################################################### --### DoIdleTasks - stuff done in any "idle" state ### --### like buffs and command processing ### --#################################################### function DoIdleTasks() local cmd = List.popleft(ResCmdList) if (cmd ~= nil) then ProcessCommand (cmd) -- ¿¹¾à ¸í·É¾î ó¸® return end if OnIdleTasks()==1 then return end if UseAutoHeal==2 then if DoHealingTasks(MyID)==1 then return end end if DoAutoBuffs(1) ~=1 then return end if (UseSacrificeOwner == 1 and SacrificeTimeout ~=-1) then if (GetTick() > SacrificeTimeout) then local skill,level= GetSacrificeSkill(MyID) if (skill <= 0) then SacrificeTimeout = -1 elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,GetV(V_OWNER,MyID),7) SacrificeTimeout = GetTick() + GetSkillInfo(skill,8,level) -- this will recast it before it drops, because the countdown starts at the start of the cast time, but duration counts down from end of cast time. return end end end if (GetV(V_MOTION,GetV(V_OWNER,MyID))==MOTION_SIT and MyState~=REST_ST and DoNotUseRest~=1) then MyState=REST_ST TraceAI("DoIdleTasks - Owner sitting, MyState -> REST_ST") return end return 1 end function DoAutoBuffs(buffmode) if GetTick() < AutoSkillTimeout then return 1 end if buffmode==2 then -- Berserk mode only buffs if BerserkMode~=1 then return 1 end end TraceAI("DoAutoBuffs"..buffmode) if (UseProvokeOwner == buffmode and ProvokeOwnerTimeout ~=-1) then if (GetTick() > ProvokeOwnerTimeout) then local skill,level= GetProvokeSkill(MyID) if (skill <= 0) then ProvokeOwnerTimeout = -1 elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then if MyASAPBuffs[3]==skill and buffmode==3 then ProvokeOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop! logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level)) else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = GetV(V_OWNER,MyID) MyPSkill = skill MyPSkillLevel = level MyPMode = 7 return OnPROVOKE_ST() end end end end if OffensiveOwnerTimeout ~=-1 then TraceAI("offensive owner ~= -1") if (GetTick() > OffensiveOwnerTimeout) then local skill,level,opt = GetOffensiveOwnerSkill(MyID) TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode) if (skill <= 0) then OffensiveOwnerTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then if MyASAPBuffs[3]==skill and buffmode==3 then OffensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop! logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level)) else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = GetV(V_OWNER,MyID) MyPSkill = skill MyPSkillLevel = level MyPMode = 11 return OnPROVOKE_ST() end end end end if DefensiveOwnerTimeout ~=-1 then TraceAI("defensive owner ~= -1") if (GetTick() > DefensiveOwnerTimeout) and (DefensiveBuffOwnerMobbed==0 or (DefensiveBuffOwnerMobbed <= GetAggroCount(GetV(V_OWNER,MyID)) and HPPercent(GetV(V_OWNER,MyID)) < DefensiveBuffOwnerHP)) then local skill,level,opt = GetDefensiveOwnerSkill(MyID) TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode) if (skill <= 0) then DefensiveOwnerTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then if MyASAPBuffs[3]==skill and buffmode==3 then DefensiveOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop! logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level)) else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = GetV(V_OWNER,MyID) MyPSkill = skill MyPSkillLevel = level MyPMode = 12 return OnPROVOKE_ST() end end end end if OtherOwnerTimeout ~=-1 then TraceAI("other owner ~= -1 "..GetTick().." "..OtherOwnerTimeout) if (GetTick() > OtherOwnerTimeout) then local skill,level,opt = GetOtherOwnerSkill(MyID) TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode) if (skill <= 0) then OtherOwnerTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then if MyASAPBuffs[3]==skill and buffmode==3 then OtherOwnerTimeout = GetTick()+20000 --We're stuck in an ASAP loop! logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level)) else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = GetV(V_OWNER,MyID) MyPSkill = skill MyPSkillLevel = level MyPMode = 13 return OnPROVOKE_ST() end end end end if (UseProvokeSelf == buffmode and ProvokeSelfTimeout ~=-1) then if (GetTick() > ProvokeSelfTimeout) then local skill,level = GetProvokeSkill(MyID) if (skill <= 0) then ProvokeSelfTimeout = -1 elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then --MyState=PROVOKE_ST --OnPROVOKE_ST() --return DoSkill(skill,level,MyID) ProvokeSelfTimeout = GetTick()+GetSkillInfo(skill,8,level) return end end end if (UseOffensiveBuff == buffmode and QuickenTimeout ~=-1) then if (GetTick() > QuickenTimeout) then local skill,level = GetQuickenSkill(MyID) if (skill<=0) then QuickenTimeout = -1 elseif level==0 then -- skill in cooldown else if (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,2) QuickenTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) UpdateTimeoutFile() return end end end end if (UseDefensiveBuff == buffmode and GuardTimeout ~=-1) then if (GetTick() > GuardTimeout) then local skill,level = GetGuardSkill(MyID) if (skill <= 0) then GuardTimeout = -1 elseif level==0 then -- skill in cooldown elseif skill==HAMI_BULWARK and UseSmartBulwark ==1 and GetV(V_MAXSP,MyID) > 160 and UseOffensiveBuff ~=0 and QuickenTimeout~=-1 then if (GetSkillInfo(skill,3,level)+120 <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,1) GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) UpdateTimeoutFile() end elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,1) GuardTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) UpdateTimeoutFile() return end end end if SOffensiveTimeout ~=-1 then if (GetTick() > SOffensiveTimeout) then local skill,level,opt = GetSOffensiveSkill(MyID) if (skill <= 0) then SOffensiveTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,4) SOffensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) --logappend("AAI_SKILL",skill.."|"..level.."|"..GetSkillInfo(skill,8,level).."|"..SOffensiveTimeout) UpdateTimeoutFile() return end end end if SDefensiveTimeout ~=-1 then TraceAI("sdefensive ~= -1") if (GetTick() > SDefensiveTimeout) then local skill,level,opt = GetSDefensiveSkill(MyID) TraceAI("timeout ok "..opt.." "..FormatSkill(skill,level).." "..buffmode) if (skill <= 0) then SDefensiveTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,5) SDefensiveTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) UpdateTimeoutFile() return end end end if SOwnerBuffTimeout ~=-1 then if (GetTick() > SOwnerBuffTimeout) then local skill,level,opt = GetSOwnerBuffSkill(MyID) if (skill <= 0) then SOwnerBuffTimeout = -1 elseif level==0 or opt ~=buffmode then -- do nothing, skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then DoSkill(skill,level,MyID,6) SOwnerBuffTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) UpdateTimeoutFile() return end end end if UseBayeriSteinWand == buffmode and SteinWandTimeout~=-1 then if GetTick() > SteinWandTimeout then if GetV(V_HOMUNTYPE,MyID)~=BAYERI then SteinWandTimeout=-1 else if (GetAggroCount(MyID) >= UseSteinWandSelfMob and UseSteinWandSelfMob~=0) or (GetAggroCount(GetV(V_OWNER,MyID)) >= UseSteinWandOwnerMob and UseSteinWandOwnerMob~=0) then DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID,10) SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel) return end end end end if (UseAutoMag == buffmode and MagTimeout ~=-1) then if (GetTick() > MagTimeout) then if (GetV(V_MERTYPE,MyID)~=4) then MagTimeout = -1 elseif (40 <= GetV(V_SP,MyID) and level ~=0) then DoSkill(MER_MAGNIFICAT,1,MyID,3) MagTimeout = GetTick() + 34000 UpdateTimeoutFile() return end end end if SightTimeout ~=-1 then TraceAI("tick:"..GetTick().."SightTimeout"..SightTimeout) if (GetTick() > SightTimeout) then local skill,level,opt = GetSightOrAoE(MyID) if (skill <= 0) then SightTimeout = -1 elseif level==0 or opt~=buffmode then -- skill in cooldown elseif (GetSkillInfo(skill,3,level) <= GetV(V_SP,MyID)) then if IsHomun(MyID)==1 then if MyASAPBuffs[3]==skill and buffmode==3 then SightTimeout = GetTick()+20000 --We're stuck in an ASAP loop! logappend("AAI_ERROR","ASAP buff attempt canceled, we were just trying to do this and it didnt work, delaying 20 seconds "..FormatSkill(skill,level)) else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = GetV(V_OWNER,MyID) MyPSkill = skill MyPSkillLevel = level MyPMode = 7 TraceAI("Using AoE skill as buff"..MyPState.." "..MyState.." "..MyPEnemy.." "..MyPSkill.." "..MyPSkillLevel) return OnPROVOKE_ST() end else DoSkill(skill,level,MyID,7) SightTimeout = AutoSkillCastTimeout + GetSkillInfo(skill,8,level) return end end end end if PainkillerFriends ~=0 then local skill,level,opt = GetDefensiveOwnerSkill(MyID) opt = PainkillerFriends if skill <=0 then --logappend("AAI_PKF","Painkiller friends disabled, don't have skill") PainkillerFriends=0 elseif level==0 or opt~=buffmode then --logappend("AAI_PKF","not time to use it") -- skill in cooldown elseif (GetSkillInfo(skill,3,level) > GetV(V_SP,MyID)) then --logappend("AAI_PKF","No SP") -- no SP else TraceAI("Painkiller Friends check") --logappend("AAI_PKF","Painkiller friends check") for k,v in pairs(Players) do --logappend("AAI_PKF","Painkiller friend "..k) if MyFriends[k]==FRIEND or MyFriends[k]==PKFRIEND then --logappend("AAI_PKF","Painkiller friend "..k.."not a friend") if PKFriendsTimeout[k]~=nil then if PKFriendsTimeout[k] < GetTick() then MyPState = MyState MyState = PROVOKE_ST MyPEnemy = k MyPSkill = skill MyPSkillLevel = level MyPMode = k return OnPROVOKE_ST() end else MyPState = MyState MyState = PROVOKE_ST MyPEnemy = k MyPSkill = skill MyPSkillLevel = level MyPMode = k return OnPROVOKE_ST() end end end end end return OnAutoBuffs(buffmode) end function DoHealingTasks (myid) rhp=HPPercent(myid) ohp=HPPercent(GetV(V_OWNER,myid)) skill,level=GetHealingSkill(myid) if skill<=0 then UseAutoHeal=0 return elseif level==0 or GetTick() < AutoSkillTimeout then return --skill in cooldown end if rhp < HealSelfHP then if skill==HVAN_CHAOTIC then if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then TraceAI("Using self Healing skill "..FormatSkill(skill,level)) if ohp < HealOwnerHP and MyState==IDLE_ST then DoSkill(skill,5,myid) else DoSkill(skill,4,myid) end return 1 else TraceAI("Can't use self healing skill, no SP") end end end if ohp < HealOwnerHP then if GetV(V_SP,myid) > GetSkillInfo(skill,3,level) then TraceAI("Using Healing skill "..FormatSkill(skill,level)) DoSkill(skill,level,GetV(V_OWNER,myid)) return 1 else TraceAI("Can't use healing skill, no SP") return end end end function UpdateTimeoutFile() if StickyStandby==2 then ShouldStandbyx=ShouldStandby else ShouldStandbyx=0 end if IsHomun(MyID)==1 then OutFile=io.open("./AI/USER_AI/data/H_"..GetV(V_OWNER,MyID).."Timeouts.lua","w") else OutFile=io.open("./AI/USER_AI/data/M_"..GetV(V_OWNER,MyID).."Timeouts.lua","w") end if OutFile~=nil then OutFile:write("MagTimeout="..TimeoutConv(MagTimeout).."\nSOffensiveTimeout="..TimeoutConv(SOffensiveTimeout).."\nSDefensiveTimeout="..TimeoutConv(SDefensiveTimeout).."\nSOwnerBuffTimeout="..TimeoutConv(SOwnerBuffTimeout).."\nGuardTimeout="..TimeoutConv(GuardTimeout).."\nQuickenTimeout="..TimeoutConv(QuickenTimeout).."\nOffensiveOwnerTimeout="..TimeoutConv(OffensiveOwnerTimeout).."\nDefensiveOwnerTimeout="..TimeoutConv(DefensiveOwnerTimeout).."\nOtherOwnerTimeout="..TimeoutConv(OtherOwnerTimeout).."\nShouldStandby="..ShouldStandbyx.."\nRegenTick[1]="..RegenTick[1].."\nMySpheres="..MySpheres.."\nEleanorMode="..EleanorMode) OutFile:close() else TraceAI("Failed to update timeout file") logappend("AAI_ERROR","Failed to update timeout file for owner "..GetV(V_OWNER,MyID)) end return end function TimeoutConv(a) if a==-1 then return 0 else return a end end function OnPROVOKE_ST() --local skill,level = GetProvokeSkill(MyID) if MyPSkillLevel==nil then logappend("AAI_ERROR","PSkillLevel is nil at start of OnPROVOKE_ST") end if MyPosX[1]==MyPosX[2] and MyPosY[1]==MyPosY[2] then SkillObjectCMDTimeout=SkillObjectCMDTimeout+1 end if IsInAttackSight(MyID,MyPEnemy,MyPSkill,MyPSkillLevel) then if MyPMode == 7 then ProvokeOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) elseif MyPMode== 9 then SightTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) elseif MyPMode== 11 then OffensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) elseif MyPMode== 12 then DefensiveOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) elseif MyPMode== 13 then OtherOwnerTimeout = GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) elseif IsPlayer(MyPMode) then -- to detect painkiller-friends, in which case PMode is the id of the friend PKFriendsTimeout[MyPMode]=GetTick()+GetSkillInfo(MyPSkill,8,MyPSkillLevel) end UpdateTimeoutFile() DoSkill(MyPSkill,MyPSkillLevel,MyPEnemy,MyPMode) MyState=MyPState TraceAI("PROVOKE_ST -> IDLE_ST - Buff/AoE okay"..MyState) MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0 return elseif SkillObjectCMDTimeout>SkillObjectCMDLimit then TraceAI("PROVOKE_ST -> IDLE_ST Couldn't get into range to provoke/AoE owner") if MyPState~=PROVOKE_ST then MyState=MyPState else MyState=0 end SkillObjectCMDTimeout=0 MyDestX,MyDestY,MyPEnemy,MyPSkill,MyPState,MyPSkillLevel,MyPMode=0,0,0,0,0,0,0 return elseif SkillObjectCMDTimeout>(SkillObjectCMDLimit/2) then --We're having trouble getting to the place we want to in order to use this buff, so let's just try to move right to target range = AttackRange(MyID,MyPSkill,MyPSkillLevel) if range > 2 and MyPEnemy==GetV(V_OWNER,MyID) then MoveToOwner(MyID) else --local x,y=ClosestR(MyID,MyPEnemy,AttackRange(MyID,MyPSkill,MyPSkillLevel),math.random(2)) local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0) Move(MyID,x,y) end return else local x,y=GetStandPoint(MyID,MyPEnemy,MyPSkill,MyPSkillLevel,0) Move(MyID,x,y) return end end function OnIDLEWALK_ST () TraceAI ("OnIDLEWALK_ST") ResetCounters() MyEnemy = 0 if SPPercent(MyID) < IdleWalkSP then MyState=IDLE_ST TraceAI ("IDLEWALK_ST -> IDLE_ST : SP is below IdleWalkSP - switching to idle mode to regen SP") return OnIDLE_ST() end if (DoIdleTasks()==nil) then return end --Targeting if SuperPassive~=1 then local object = SelectEnemy(GetFriendTargets()) if (object ~= 0) then -- MYOWNER_ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("IDLEWALK_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then OnCHASE_ST() end return end if (HPPercent(MyID) > AggroHP and (SPPercent(MyID) > AggroSP or AggroSP==0) and (ShouldStandby == 0 or StickyStandby ==0)) then aggro=1 else aggro=0 end object=SelectEnemy(GetEnemyList(MyID,aggro)) if object~=0 then MyState = CHASE_ST MyEnemy = object TraceAI ("IDLEWALK_ST -> CHASE_ST : ATTACKED_IN") if (FastChangeCount < FastChangeLimit and FastChange_I2C ==1) then return OnCHASE_ST() end return end if (aggro==1 and TankMonsterCount < TankMonsterLimit) then object = SelectEnemy(GetEnemyList(MyID,-1)) if (object ~= 0) then MyState = TANKCHASE_ST MyEnemy = object TraceAI ("IDLEWALK_ST -> TANKCHASE_ST") return end end end --Following local distance = GetDistanceAR(MyID,GetV(V_OWNER,MyID)) if ( distance > GetMoveBounds()) then -- MYOWNER_OUTSIGNT_IN MyState = FOLLOW_ST TraceAI ("IDLEWALK_ST -> FOLLOW_ST: Way too far away from owner "..distance) return end if UseAutoHeal==3 then if DoHealingTasks(MyID) == 1 then return end end DoAutoBuffs(-2) -- end of the usual idle tasks - now we have to do the idle walk part of it, which sucks. local x,y=GetV(V_POSITION,MyID) local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) local motion=GetV(V_MOTION,MyID) if (GetDistanceAPR(MyID,MyDestX,MyDestY)>=1) or IdleWalkTries > 6 or GetDistanceAPR(GetV(V_OWNER,MyID),MyDestX,MyDestY) > GetMoveBounds() then if OldHomunType==AMISTR and UseCastleRoute==1 and (UseIdleWalk==5 or UseIdleWalk==6) and RelativeRoute==0 and GetDistanceP(x,y,ox,oy) > 2 then if GetTick() > AutoSkillTimeout then DoSkill(HAMI_CASTLE,5,MyID) TraceAI("Castle Route: Casting castling on self") else TraceAI("Castle Route: Skill timeout not OK - can't cast castling") end return elseif UseCastleRoute==1 and OldHomunType==AMISTR and RelativeRoute==0 then TraceAI("We're set to use castling route, but can't, UseIdleWalk="..UseIdleWalk.." distance: "..GetDistanceP(x,y,ox,oy)) end MyDestX,MyDestY=GetIdleWalkDest(MyID) if MyDestX==ox and MyDestY==oy then MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1) end IdleWalkTries=0 TraceAI("IDLEWALK_ST: New destination: "..MyDestX..","..MyDestY) return Move(MyID,MyDestX,MyDestY) elseif motion == MOTION_STAND then if IdleWalkTries == 4 then MyDestX,MyDestY=Closest(MyID,MyDestX,MyDestY,1,1) TraceAI("IDLEWALK_ST: Having a bit of trouble - adjust dest to: "..MyDestX..","..MyDestY) end TraceAI("IDLEWALK_ST: No move after "..IdleWalkTries.." trying again:"..MyDestX..","..MyDestY) IdleWalkTries=IdleWalkTries+1 return Move(MyID,MyDestX,MyDestY) else --we're en route return end end function GetIdleWalkDest(MyID) local mx,my=GetV(V_POSITION,MyID) local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) local xoff,yoff=mx-ox,my-oy TraceAI("Idlewalk"..xoff..","..yoff) if UseIdleWalk==1 then --orbit local stepsize = 30 if IdleWalkDistance > 4 then stepsize=30 else stepsize=45 end local temp=math.deg(math.atan2(xoff,yoff))+stepsize/2 if temp < 0 then temp=temp+360 end local step = math.floor(temp/stepsize) + 1 local angle=math.rad(step*stepsize) local destx=math.ceil(math.sin(angle)*IdleWalkDistance)+ox local desty=math.ceil(math.cos(angle)*IdleWalkDistance)+oy TraceAI("Orbit Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my) return destx,desty elseif UseIdleWalk==2 then -- Cross local temp=math.deg(math.atan2(xoff,yoff))+45 if temp < 0 then temp=temp+360 end step = math.floor(temp/90) TraceAI("Cross step "..step) if step == 0 then -- north goes to south return ox,oy-IdleWalkDistance elseif step == 1 then -- east goes to north return ox,oy+IdleWalkDistance elseif step == 2 then -- south goes to west return ox-IdleWalkDistance,oy else -- must be west! return ox+IdleWalkDistance,oy end elseif UseIdleWalk==3 then -- Rectangle local temp=math.deg(math.atan2(xoff,yoff))+22.5 local tempt=temp if temp < 0 then temp=temp+360 end local step = math.floor(temp/45) + 1 local destx,desty = ox,oy if step > 0 and step < 4 then destx = ox + IdleWalkDistance elseif step >4 and step < 8 then destx = ox - IdleWalkDistance end if step ==8 or step == 1 or step == 7 then desty= oy + IdleWalkDistance elseif step > 2 and step < 6 then desty=oy-IdleWalkDistance end TraceAI("Rectangle Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." temp: "..temp.." "..tempt.." step: "..step) return destx,desty elseif UseIdleWalk==4 then -- Random local step = math.random(0,359) local angle=math.rad(step) local destx=absceil(math.sin(angle)*IdleWalkDistance)+ox local desty=absceil(math.cos(angle)*IdleWalkDistance)+oy TraceAI("Random Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my) return destx,desty elseif UseIdleWalk==5 or UseIdleWalk==6 then -- Route walk local routelen=1 local step = nil local dist = 999 local posx,posy if RelativeRoute==1 then posx,posy=xoff,yoff TraceAI("Relative route "..xoff..","..yoff) else TraceAI("Absolute route "..mx..","..my) posx,posy=mx,my end for k,v in pairs(MyRoute) do routelen=k if v[1]==posx and v[2]==posy then step=k dist=0 TraceAI("Route Analysis: on route cell "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." current step "..step.."/"..dist) else local distance=math.sqrt((v[1]-posx)^2+(v[2]-posy)) if distance < dist then dist=distance step=k end TraceAI("Route Analysis: "..posx..","..posy.." route step: "..k.." "..v[1]..","..v[2].." distance "..distance.." current step "..step.."/"..dist) end end -- now we're at position 'step' local nextstep if RouteWalkDirection==1 and step==routelen then if UseIdleWalk==5 then RouteWalkDirection=-1 nextstep=step-1 else nextstep=1 end elseif RouteWalkDirection==-1 and step==1 then if UseIdleWalk==5 then RouteWalkDirection=1 nextstep=2 else nextstep=routelen end else nextstep=step+1*RouteWalkDirection end TraceAI("Route Walk - nextstep "..nextstep.." from "..step) local destx,desty if RelativeRoute==1 then destx,desty = ox+MyRoute[nextstep][1],oy+MyRoute[nextstep][2] else destx,desty = MyRoute[nextstep][1],MyRoute[nextstep][2] end TraceAI("Route Dest: "..destx..","..desty.." owner: "..ox..","..oy.." mypos: "..mx..","..my.." pos: "..posx..","..posy.." routelen: "..routelen.." "..dist.." step: "..step.." nextstep= "..nextstep) return destx,desty else logappend("AAI_ERROR","Invalid UseIdleWalk made it all the way to GetIdleWalkDest()") UseIdleWalk=0 return GetV(V_POSITION,MyID) end end --###################### --### DoAutoPushback ### --###################### function DoAutoPushback(myid) local actors = GetActors() local x,y=GetV(V_POSITION,myid) for i,v in ipairs(actors) do if (IsMonster(v)==1) then local tact= GetTact(TACT_PUSHBACK,v) local target =GetV(V_TARGET,v) if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then TraceAI("Pushback: "..GetDistanceA(target,v)) if (GetDistanceA(target,v) <= AutoPushbackThreshold and GetDistanceA(target,v)~=-1) then TraceAI("Enemies close to me, using pushback") local skill,level=GetPushbackSkill(MyID) if (skill<=0) then UseAutoPushback=0 elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level) and GetV(V_SKILLATTACKRANGE,MyID,skill) >= GetDistanceA(myid,v)) then DoSkill(skill,level,v) return end break end end elseif (IsFriendOrSelf(id)==0 and PVPmode ~=0) then local tact= GetPVPTact(TACT_PUSHBACK,v) local target =GetV(V_TARGET,v) if (tact==2 or (tact==1 and IsFriendOrSelf(target)==1))then if (GetDistanceA(target,v) <= AutoPushbackThreshold) then TraceAI("Enemies close to me, using pushback") local skill,level=GetPushbackSkill(MyID) if (skill<=0) then UseAutoPushback=0 elseif (GetV(V_SP,myid) >= GetSkillInfo(skill,3,level)) then DoSkill(skill,level,v) return end break end end end end return 1 end --#################### --### DoKiteAdjust ### --#################### function DoKiteAdjust(myid,enemy) local step local target=GetV(V_TARGET,enemy) if (IsFriend(target)==1 or target==MyID) then step=KiteStep else step=KiteParanoidStep end local x,y=GetV(V_POSITION,myid) local ox,oy=GetV(V_POSITION,GetV(V_OWNER,myid)) local ex,ey=GetV(V_POSITION,enemy) local xoptions ={[2]=1,[0]=1,[1]=1} local yoptions ={[2]=1,[0]=1,[1]=1} local xdirection,ydirection=0,0 if (x > ex) then xoptions[2]=0 elseif (x < ex) then xoptions[1]=0 else yoptions[0]=0 end if (y > ey) then yoptions[2]=0 elseif (y < ey) then yoptions[1]=0 else xoptions[0]=0 end if (ox > x) then if (xoptions[1]==1) then xdirection=1 elseif (xoptions[0]==1) then xdirection=0 elseif (xoptions[2]==1 and (ox-x+step) <= KiteBounds) then xdirection=-1 elseif (ey < y) then xdirection=0 else xdirection=1 end else if (xoptions[2]==1) then xdirection=-1 elseif (xoptions[0]==1) then xdirection=0 elseif (xoptions[1]==1 and (x-ox+step) <= KiteBounds) then xdirection=1 elseif (ey > y) then xdirection=0 else xdirection=-1 end end if (oy > y) then if (yoptions[1]==1) then ydirection=1 elseif (yoptions[0]==1 and xdirection~=0) then ydirection=0 elseif (yoptions[2]==1 and oy-y+step <= step) then ydirection=-1 elseif (ex > x and xdirection~=0) then ydirection=0 else ydirection=1 end else if (yoptions[2]==1) then ydirection=-1 elseif (yoptions[0]==1 and xdirection~=0) then ydirection=0 elseif (yoptions[1]==1 and y-oy+step <= KiteBounds) then ydirection=1 elseif (ex < x and xdirection~=0) then ydirection=0 else ydirection=-1 end end TraceAI("Kiteing in "..xdirection..","..ydirection.." direction") MyDestX=x+step*xdirection MyDestY=y+step*ydirection Move(myid,MyDestX,MyDestY) end function FailSkillUse(mode) local modex=mode if IsPlayer(mode)==1 then mode=13 end if SkillFailCount[mode]==nil then SkillFailCount[mode]=0 end if SkillFailCount[mode] < SkillRetryLimit[mode] then if mode == -1 then -- attack state MySkillUsedCount=math.max(0,MySkillUsedCount-1) elseif mode==1 then GuardTimeout=1 elseif mode==2 then QuickenTimeout=1 elseif mode==3 then MagTimeout=1 elseif mode==4 then SOffensiveTimeout=1 elseif mode==5 then SDefensiveTimeout=1 elseif mode==6 then SOwnerBuffTimeout=1 elseif mode==7 then SightTimeout=1 elseif mode==9 then ProvokeOwnerTimeout=1 elseif mode==10 then SteinWandTimeout = 1 elseif mode==11 then OffensiveOwnerTimeout = 1 elseif mode==12 then DefensiveOwnerTimeout = 1 elseif mode==13 then OtherOwnerTimeout = 1 elseif IsPlayer(modex) then --logappend("AAI_PKF","skill failed on"..mode) PKFriendsTimeout[modex]=1 else OnFailUnknownMode(mode) end TraceAI("Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again") logappend("AAI_SKILLFAIL","Skill cast appears to have failed: Mode "..mode.." fail count "..SkillFailCount[mode].." will try again") SkillFailCount[mode]=SkillFailCount[mode]+1 else TraceAI("Skill cast appears to have failed, but we're past the retry limit, so screw it: Mode "..mode.." fail count "..SkillFailCount[mode]) logappend("AAI_SKILLFAIL","Skill cast appears to have failed, but we're past the retry limit, so screw it: Mode "..mode.." fail count "..SkillFailCount[mode]) SkillFailCount[mode]=0 end AutoSkillTimeout = 1 AutoSkillCastTimeout = 1 if AutoSkillCooldown[CastSkill]~=nil then AutoSkillCooldown[CastSkill]=1 end CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 end --######################## --### Main AI Function ### --######################## function AI(myid) MyID = myid if LastAITime==GetTick() then --prevent AI from running twice in the same tick. TraceAI("double-AI() call detected, blocked") return else if LastAITime + 400 < GetTick() and LastAITime > 10 then TraceAI("Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago") logappend("AAI_SKILLFAIL", "Missed AI calls. Previous AI call was "..LastAITime-GetTick().." ms ago") end LastAIDelay=GetTick()-LastAITime LastAITime=GetTick() if MyEnemy ~= 0 then local ex,ey=GetV(V_POSITION,MyEnemy) for v=10,2,-1 do EnemyPosX[v],EnemyPosY[v]=EnemyPosX[v-1],EnemyPosY[v-1] end EnemyPosX[1],EnemyPosY[1]=ex,ey,GetV(V_MOTION,MyID) end end if DoneInit== 0 then doInit(myid) end if AggressiveRelogTracking==1 then if IsHomun(MyID)==1 then OutFile=io.open(AggressiveRelogPath.."H_"..GetV(V_OWNER,MyID).."Time.lua","w") else OutFile=io.open(AggressiveRelogPath.."M_"..GetV(V_OWNER,MyID).."Time.lua","w") end if OutFile~=nil then OutFile:write("LastAITime_ART="..LastAITime) OutFile:close() else TraceAI("Failed to update time file for Aggressive Relog Tracking") logappend("AAI_ERROR","Failed to update aggressive relog time tracking file for owner "..GetV(V_OWNER,MyID)) end end OnAIstart() -- ###AUTOFRIEND### -- Save the ID to a file so counterpart can friend it -- Why is it here instead of at the start? -- Because the client wont tell us our ID until AI() is called :-( if (NeedToDoAutoFriend==1 and NewAutoFriend==1) then TraceAI("Now it's time to do the autofriend") local owner=GetV(V_OWNER,myid) if (IsHomun(myid)==1) then OutFile=io.open("AI/USER_AI/data/H_"..owner..".txt","w") else OutFile=io.open("AI/USER_AI/data/M_"..owner..".txt","w") end if OutFile~=nil then OutFile:write (myid) OutFile:close() else TraceAI("Failed to create autofriending file.") logappend("AAI_ERROR","Failed to create autofriending file for owner "..GetV(V_OWNER,MyID)) end NeedToDoAutoFriend=0 end --###BOOKKEEPING### -- Hackjob to fix strange behavior, with the timeouts being set to hours, days, or weeks in the future (suspect due to GetTick() returning bad numbers). if GuardTimeout-GetTick() > 350000 then logappend("AAI_ERROR","Guard timeout was "..GuardTimeout.." time is "..GetTick()) GuardTimeout=1 end if MagTimeout-GetTick() > 350000 then logappend("AAI_ERROR","Mag timeout was "..MagTimeout.." time is "..GetTick()) MagTimeout=1 end if QuickenTimeout-GetTick() > 1205000 then logappend("AAI_ERROR","Quicken timeout was "..QuickenTimeout.." time is "..GetTick()) QuickenTimeout=1 end if SOffensiveTimeout-GetTick() > 350000 then logappend("AAI_ERROR","Homun S offensive timeout was "..SOffensiveTimeout.." time is "..GetTick()) SOffensiveTimeout=1 end if SDefensiveTimeout-GetTick() > 350000 then logappend("AAI_ERROR","Homun S defensive timeout was "..SDefensiveTimeout.." time is "..GetTick()) SDefensiveTimeout=1 end if SOwnerBuffTimeout-GetTick() > 6000000 then logappend("AAI_ERROR","Homun S owner buff timeout was "..SOwnerBuffTimeout.." time is "..GetTick()) SOwnerBuffTimeout=1 end if OffensiveOwnerTimeout-GetTick() > 6000000 then logappend("AAI_ERROR","Offensive owner buff timeout was "..OffensiveOwnerTimeout.." time is "..GetTick()) OffensiveOwnerTimeout=1 end if DefensiveOwnerTimeout-GetTick() > 6000000 then logappend("AAI_ERROR","Defensive owner buff timeout was "..DefensiveOwnerTimeout.." time is "..GetTick()) DefensiveOwnerTimeout=1 end if OtherOwnerTimeout-GetTick() > 6000000 then logappend("AAI_ERROR","Other owner buff timeout was "..OtherOwnerTimeout.." time is "..GetTick()) OtherOwnerTimeout=1 end if MyMaxSP~=GetV(V_MAXSP,MyID) then AdjustCapriceLevel() MyMaxSP=GetV(V_MAXSP,MyID) end if AIInitTick==0 or AIInitTick==1 or AIInitTick==nil then AIInitTick=GetTick() end FastChangeCount = 0 --###DATA GATHERING### for k,v in pairs(Unreachable) do if (IsOutOfSight(myid,k)==true or GetV(V_MOTION,k)==MOTION_DEAD or (IsFriendOrSelf(GetV(V_TARGET,k))==1) and GetV(V_MOTION,k)~=MOTION_STAND) then Unreachable[k]=nil TraceAI("Marking as reachable"..k) end end -- Position sensing local x,y=GetV(V_POSITION,MyID) local ox,oy=GetV(V_POSITION,GetV(V_OWNER,MyID)) if MyEnemy ~= 0 then local ex,ey=GetV(V_POSITION,MyEnemy) else ex,ey=0,0 end --myposlog="" for v=10,2,-1 do MyASAPBuffs[v],MyPosX[v],MyPosY[v],OwnerPosX[v],OwnerPosY[v],EnemyPosX[v],EnemyPosY[v],MyMotions[v],MyStates[v],MyEnemies[v]=MyASAPBuffs[v-1],MyPosX[v-1],MyPosY[v-1],OwnerPosX[v-1],OwnerPosY[v-1],EnemyPosX[v-1],EnemyPosY[v-1],MyMotions[v-1],MyStates[v-1],MyEnemies[v-1] end MyPosX[1],MyPosY[1],OwnerPosX[1],OwnerPosY[1],EnemyPosX[1],EnemyPosY[1],MyMotions[1],MyStates[1],MyEnemies[1]=x,y,ox,oy,ex,ey,GetV(V_MOTION,MyID),MyState,MyEnemy if MyState==PROVOKE_ST then MyASAPBuffs[1]=MyASAPBuffs[2] else MyASAPBuffs[1]=0 end --for v=1,10 do -- myposlog=myposlog.." "..MyPosX[v]..","..MyPosY[v] --end --TraceAI("MyPosLog "..myposlog.." x="..x.."y="..y) if x~=MyPosX[2] or y~=MyPosY[2] then --TraceAI("we moved") LastMovedTime=GetTick() end -- Actor list preprocessing local actors=GetActors() Actors={} OldPlayers=Players Players={} Monsters={} Summons={} Retainers={} Targets={} TankMonsterCount=0 local seralegionCount=0 SeraLegionList="" local seralegionActive=0 local seralegionBugged=0 for i,v in ipairs(actors) do local x,y = GetV(V_POSITION,v) if AAIActors[v]~=1 then if IsHomun(myid)==1 then logappend("AAI_ACTORS","Actor "..v.." type "..GetV(V_HOMUNTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v)) else logappend("AAI_ACTORS","Actor "..v.." mertype "..GetV(V_MERTYPE,v).." at "..x..","..y.." Is M="..IsMonster(v)) end AAIActors[v]=1 end local x,y = GetV(V_POSITION,v) if GetV(V_HOMUNTYPE,v)==1102 and x==174 and 33==y and GetV(V_MOTION,v)==MOTION_STAND then -- do nothing else if (false == IsOutOfSight(myid,v)) then Actors[v]=1 --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." in sight") if GetV(V_HOMUNTYPE,MyID)==SERA then local actortype=GetV(V_HOMUNTYPE,v) if (actortype > 2157 and actortype < 2161) then seralegionCount=seralegionCount+1 SeraLegionList=SeraLegionList..v.." " if GetV(V_MOTION,v)==MOTION_STAND and GetDistanceAR(MyID,v)>3 then seralegionBugged=seralegionBugged+1 else seralegionActive=seralegionActive+1 end end end if (v > MagicNumber2) then Players[v]=1 if MyFriends[v]==FRIEND and GetV(V_OWNER,MyID)~=v then --Newly appeared on screen if OldPlayers[v]~=1 or AggressiveAutofriend then local newfriendhidfile=io.open("./AI/USER_AI/data/H_"..v..".txt","r") local newfriendmidfile=io.open("./AI/USER_AI/data/M_"..v..".txt","r") TraceAI("new friend on screen, checking H_ID"..v) if newfriendhidfile~=nil then TraceAI("h_id found"..v) local newfriendhid=newfriendhidfile:read("*line") if newfriendhid~=nil then TraceAI("h_id: "..newfriendhid) MyFriends[newfriendhid+1-1]=RETAINER --crude type conversion end newfriendhidfile:close() end if newfriendmidfile~=nil then TraceAI("new friend on screen, checking M_ID"..v) local newfriendmid=newfriendmidfile:read("*line") TraceAI("m_id found"..v) if newfriendmid~=nil then TraceAI("m_id: "..newfriendmid) MyFriends[newfriendmid+1-1]=RETAINER end newfriendmidfile:close() end end end elseif IsMonster(v)==1 then --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." is a monster") Monsters[v]=1 if (v < MagicNumber) then Summons[v]=1 end if (AutoDetectPlant==1 and IsActive[v]~=1 and IsHomun(myid)~=1) then if (GetV(V_MOTION,v)==MOTION_STAND or GetV(V_MOTION,v)==MOTION_DAMAGE or GetV(V_MOTION,v)==MOTION_DEAD) then IsActive[v]=0 else IsActive[v]=1 end end if (GetTact(TACT_BASIC,v)==TACT_TANK and GetV(V_TARGET,v)==MyID) then TankMonsterCount=TankMonsterCount+1 end motionclass=MotionClassLU[GetV(V_MOTION,v)] if motionclass==nil then motionclass=3 end --CHANGE --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." "..motionclass) if (motionclass~=-1 and (IsActive[v]==1 or AutoDetectPlant~=1 or IsHomun(myid)==1)) then --TraceAI(v.." of type "..GetV(V_HOMUNTYPE,v).." target added") Targets[v]={motionclass,GetTargetClass(GetV(V_TARGET,v))} end elseif (v < MagicNumber) then Retainers[v]=1 end end end end if GetV(V_HOMUNTYPE,MyID)==SERA then for t=1,4 do SeraLegionTotalHist[t+1]=SeraLegionTotalHist[t] SeraLegionBugHist[t+1]=SeraLegionBugHist[t] end SeraLegionTotalHist[1]=seralegionCount SeraLegionBugHist[1]=seralegionBugged SeraLegionCount=math.max(SeraLegionTotalHist[1],SeraLegionTotalHist[2],SeraLegionTotalHist[3],SeraLegionTotalHist[4],SeraLegionTotalHist[5]) SeraLegionBugged=math.min(SeraLegionBugHist[1],SeraLegionBugHist[2],SeraLegionBugHist[3],SeraLegionBugHist[4],SeraLegionBugHist[5]) SeraLegionActive=SeraLegionCount-SeraLegionBugged if SeraLegionCount==0 and GetTick() > AutoSkillTimeout and GetTick() < AutoSkillCooldown[MH_SUMMON_LEGION] then --Why are we in cooldown when there are no bugs out? Cast must have failed, or something killed them. AutoSkillCooldown[MH_SUMMON_LEGION]=1 TraceAI("Sera Legion: Legion cooldown canceled, bugs not present") elseif SeraLegionBugged > 2 then AutoSkillCooldown[MH_SUMMON_LEGION]=1 TraceAI("Sera Legion: Legion cooldown canceled, bugs bugged") end --logappend("AAI_Legion","Sera Legion - "..SeraLegionCount.." bugs out "..SeraLegionActive.." active and "..SeraLegionBugged.." bugged. "..SeraLegionList) end if PVPmode==1 then for k,v in pairs(Players) do --logappend("AAI_PVP",k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k)) TraceAI("PVP: Player "..k.." ("..GetV(V_HOMUNTYPE,k)..") Motion: "..FormatMotion(GetV(V_MOTION,k)).."Is monster? "..IsMonster(k)) if IsMonster(k)==1 then Targets[k] = {MotionClassLU[GetV(V_MOTION,k)],GetTargetClass(GetV(V_TARGET,k))} end end end --New autofriend routine if (NewAutoFriend==1 and AssumeHomun==1) then friendedOK=0 retainercount=0 for k,v in pairs(Retainers) do if (k~=MyID) then retainercount=retainercount+1 if (MyFriends[k]==2) then friendedOK=1 end end end if (friendedOK==0) then local owner=GetV(V_OWNER,MyID) if (IsHomun(myid)==1) then InFile=io.open("./AI/USER_AI/data/M_"..owner..".txt","r") else InFile=io.open("./AI/USER_AI/data/H_"..owner..".txt","r") end if InFile~=nil then retainerid=InFile:read("*a") InFile:close() retainerid=tonumber(retainerid) if (retainerid ~=nil) then MyFriends[retainerid]=2 end end end end --SP Watcher local sp = GetV(V_SP,MyID) if CastSkill~=0 then local castskillold = CastSkill local castskilloldlvl = CastSkillLevel -- remember these for the SP watcher down below. local skillcastingtime = GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel)*CastTimeRatio logappend("AAI_SKILLFAIL", "Skillfail Watcher active: "..FormatSkill(CastSkill,CastSkillLevel).."mode"..CastSkillMode.."delay "..LastAIDelay.." motion "..GetV(V_MOTION,MyID)) if GetSkillInfo(CastSkill,4,CastSkillLevel)+GetSkillInfo(CastSkill,5,CastSkillLevel) == 0 then if LastAIDelay > 500 then --Skill cast successfully TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) SkillFailCount[CastSkillMode]=0 CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 LastASAPTargBuffTime =0 LastASAPTargBuffTarg =0 LastASAPTargBuffMode =0 elseif GetTick() - CastSkillTime > 1000 then --Musta failed, we tried to use an instant cast skill 1 second ago and still no sign of it and no sign of delay. TraceAI("Delay watcher: no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel)) logappend("AAI_SKILLFAIL", "no sign of instacast skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel)) FailSkillUse(CastSkillMode) end else if GetV(V_MOTION,MyID)==MOTION_CASTING then CastSkillState=1 elseif CastSkillState>0 then if LastAIDelay > 220 then --Skill cast successfully if CastSkillMode==8 then if EleanorMode==1 then EleanorMode=0 else EleanorMode=1 end UpdateTimeoutFile() end TraceAI("Delay watcher: Skill use successful detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) logappend("AAI_SKILLFAIL","successful skill use detected by delay - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) SkillFailCount[CastSkillMode]=0 CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 else CastSkillState=CastSkillState+1 if CastSkillState > 2 then local flinch=0 for v=1,10,1 do if MyMotions[v]==MOTION_DAMAGE then flinch=1 break end end if flinch==1 or skillcastingtime + CastSkillTime < GetTick() then TraceAI("Delay watcher: Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime)) logappend("AAI_SKILLFAIL", "Skill casting detected, but no delay - cast was broken: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." flinch: "..flinch.." expected cast completion: "..(skillcastingtime + CastSkillTime)) FailSkillUse(CastSkillMode) elseif skillcastingtime + CastSkillTime > GetTick() then TraceAI("Delay watcher: Skill use successful detected without delay. mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) logappend("AAI_SKILLFAIL","Skill use successful detected without delay. mode"..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) SkillFailCount[CastSkillMode]=0 CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 end end end else -- CastSkillState is 0, so the skill hasn't started casting, but just in case it slipped by: if LastAIDelay > 220 then --Skill cast successfully if CastSkillMode==8 then if EleanorMode==1 then EleanorMode=0 else EleanorMode=1 end UpdateTimeoutFile() end TraceAI("Delay watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) logappend("AAI_SKILLFAIL","successful skill use detected - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) SkillFailCount[CastSkillMode]=0 CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 elseif GetTick() - CastSkillTime > 1000 then TraceAI("Delay watcher: no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel)) logappend("AAI_SKILLFAIL", "no sign of skill casting 1 second later - failed. mode: "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel)) FailSkillUse(CastSkillMode) end end end -- sp watcher if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) or MyLastSP - sp == GetSkillInfo(castskillold,3,castskilloldlvl) then --if IsHomun(myid)==0 then TraceAI("SP watcher: Skill use successful - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) logappend("AAI_SKILLFAIL","successful skill use detected by SP use - mode "..CastSkillMode.." skill "..FormatSkill(CastSkill,CastSkillLevel).." LastAIDelay "..LastAIDelay) SkillFailCount[CastSkillMode]=0 CastSkill=0 CastSkillLevel=0 CastSkillMode=0 CastSkillTime=0 CastSkillState=0 --end if sp-MyLastSP == RegenTick[1] - GetSkillInfo(castskillold,3,castskilloldlvl) then LastSPTime=GetTick() end elseif MyLastSP<= sp then if sp-MyLastSP==RegenTick[1] and RegenTick[1]~=0 then LastSPTime=GetTick() end end else if sp-MyLastSP == RegenTick[1] and RegenTick[1]~=0 then LastSPTime=GetTick() elseif sp-MyLastSP > 100 then -- do nothing, owner aid-potted it, ignore this elseif sp-MyLastSP > RegenTick[1] or (sp-MyLastSP < RegenTick[1] and sp-MyLastSP > 0) then LastSPTime=GetTick() if sp-MyLastSP > math.floor(GetV(V_MAXSP,MyID)/100)+3 then -- this could be the correct tick to use for v=3,2,-1 do RegenTick[2][v]=RegenTick[2][v-1] end RegenTick[2][1]=sp-MyLastSP local tickcount=0 for k,v in pairs(RegenTick[2]) do if v~=sp-MyLastSP then break end tickcount=k end if tickcount==3 then RegenTick[1]=sp-MyLastSP TraceAI("Watcher: New SP Regen tick set: "..RegenTick[1]) logappend("AAI_SKILLFAIL","New SP Regen tick set: "..RegenTick[1]) UpdateTimeoutFile() end end end end MyLastSP=sp --###COMMAND PROCESSING### local msg = GetMsg (myid) -- command local rmsg = GetResMsg (myid) -- reserved command if msg[1] == NONE_CMD then if rmsg[1] ~= NONE_CMD then if List.size(ResCmdList) < 10 then List.pushright (ResCmdList,rmsg) end end else List.clear (ResCmdList) -- »õ·Î¿î ¸í·ÉÀÌ ÀÔ·ÂµÇ¸é ¿¹¾à ¸í·ÉµéÀº »èÁ¦ÇÑ´Ù. ProcessCommand (msg) -- ¸í·É¾î ó¸® end OnAImiddle() --###EMERGENCY HANDLING### -- Avoid Routine if (IsHomun(MyID)==1 and UseAvoid==1 and (GetTick()-AIInitTick) > 5000) then for i,v in ipairs(actors) do if MyAvoid[GetV(V_HOMUNTYPE,v)]==1 then os.exit() end end end if UseAutoHeal==1 then if DoHealingTasks(MyID) == 1 then return end end -- Prevent from being left behind -- Used only in critical (merc out of MoveBounds) situations -- Otherwise, IDLE_ST handles it local dist2owner=GetDistanceRect(MyID,GetV(V_OWNER,MyID)) if (MyState ~=FOLLOW_ST and dist2owner > GetMoveBounds()) then MyState=FOLLOW_ST end --Cancel all action during the spawn invulnerability if (GetTick() < (MyStart + SpawnDelay)) then return end if OldHomunType==AMISTR and UseCastleDefend ==1 then if GetTick() > AutoSkillTimeout then local mytargetweight =0 local ownertargetweight =0 for k,v in pairs(Targets) do if v[2]==1 then mytargetweight=mytargetweight+GetTact(TACT_WEIGHT,k) elseif v[2]==2 then if GetV(V_TARGET,k)==GetV(V_OWNER,MyID) then ownertargetweight=ownertargetweight+GetTact(TACT_WEIGHT,k) end end end if CastleDefendThreshold <= ownertargetweight and ownertargetweight > mytargetweight then DoSkill(HAMI_CASTLE,5,MyID) end end end object = SelectEnemy(GetEnemyList(MyID,-2)) if (object~=0 and object ~= MyEnemy and MyState~=FOLLOW_ST) then MyEnemy=object MyState=CHASE_ST TraceAI("RESCUE ACTIVATED - Targeting "..object) end -- New in 1.51 - specialized cast react tactics. for k,v in pairs(Targets) do if v[2]==1 or v[2] == 2 then tactcast= GetTact(TACT_CAST,k) if tactcast > CAST_REACT then local skill,level=0,0 if tactcast > 1000 then for ii,vv in ipairs(GetTargetedSkills()) do if vv[2]==tactcast and vv[3]~=0 and vv[3]~=nil then skill=vv[2] level=vv[3] break end end else -- generic skill response. for ii,vv in ipairs(GetTargetedSkills()) do if tactcast==9 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then skill=vv[2] level=vv[3] break elseif tactcast==vv[1]-10 and vv[2]~=0 and vv[3]~=0 and vv[3] ~=nil then skill=vv[2] level=vv[3] break end end end if skill~=0 then MySkill=skill MySkillLevel=level MyEnemy=k MyState=OnSKILL_OBJECT_CMD_ST TraceAI("CAST_REACT_(skill) being enabled against target"..k.." with tactic "..tactcast.." with "..FormatSkill(skill,level)) break end elseif tactcast == CAST_REACT_STIEN then if GetV(V_HOMUNTYPE,MyID)==BAYERI then if AutoSkillTimeout < GetTick() and GetSkillInfo(MH_STEINWAND,3,5) < GetV(V_SP,MyID) then DoSkill(MH_STEINWAND,5,MyID) TraceAI("CAST_REACT_STIEN being enabled against target"..k.." with tactic "..tactcast) return end end elseif tactcast == CAST_REACT_CASTLE then if (modulo(GetV(V_HOMUNTYPE,MyID),4)==AMISTR or OldHomunType==AMISTR) and v[2]==2 then if AutoSkillTimeout < GetTick() and GetSkillInfo(HAMI_CASTLE,3,5) < GetV(V_SP,MyID) then DoSkill(HAMI_CASTLE,5,MyID) TraceAI("CAST_REACT_CASTLE being enabled against target"..k.." with tactic "..tactcast) return end end end end end if MyState~=PROVOKE_ST then if DoAutoBuffs(3) ~= 1 then logappend("AAI_ASAP","ASAP BUFF "..MyState) if MyState==PROVOKE_ST then --if we're looking at a targeted buff, this means it's an ASAP poison mist, painkiller, or lava slide MyASAPBuffs[1]=MyPSkill end return end end -- Check to see if mercenary should activate kiting routine if ((KiteMonsters==1 and (KiteOK(MyID)==1 or ForceKite == 1)) or HPPercent(MyID) < FleeHP) then local x,y=GetV(V_POSITION,MyID) if ((x==KiteDestX and y==KiteDestY) or (KiteDestX==0 and KiteDestY==0)) then local actors = GetActors() kitecalled=0 for i,v in ipairs(actors) do if (IsMonster(v)==1 and GetV(V_MOTION,v) ~= MOTION_DEAD) then local target=GetV(V_TARGET,v) local tact = GetTact(TACT_KITE,v) if ((IsFriendOrSelf(target)==1 and tact==1) or tact==2) then local threshold if (IsFriend(target)==1 or target==MyID) then threshold=KiteThreshold else threshold=KiteParanoidThreshold end if (GetDistanceA(MyID,v) <= threshold) then TraceAI("Enemies close to me, start kite routine. ") DoKiteAdjust(MyID,v) kitecalled=1 break end end end end if (kitecalled == 0) then KiteDestX,KiteDestY=0,0 end else Move(myid,KiteDestX,KiteDestY) end end --Stienwand autocast on tele stuff if GetTick() < SteinWandPauseTime then return end if UseSteinWandTele ==1 and GetV(V_HOMUNTYPE,MyID)==BAYERI then if SteinWandPauseTime == 0 then DoSkill(MH_STEINWAND,BayeriSteinWandLevel,MyID) SteinWandTimeout=AutoSkillCastTimeout+GetSkillInfo(MH_STEINWAND,8,BayeriSteinWandLevel) SteinWandPauseTime=SteinWandTelePause+GetTick() return end end --###STATE PROCESSES### --TraceAI("SP tracking: Time: "..GetTick().." last moved: "..LastMovedTime.." last sp time "..LastSPTime) if (MyState == IDLE_ST) then OnIDLE_ST () elseif (MyState == CHASE_ST) then OnCHASE_ST () elseif (MyState == ATTACK_ST) then OnATTACK_ST () elseif (MyState == FOLLOW_ST) then OnFOLLOW_ST () elseif (MyState == MOVE_CMD_ST) then OnMOVE_CMD_ST () elseif (MyState == STOP_CMD_ST) then OnSTOP_CMD_ST () elseif (MyState == ATTACK_OBJECT_CMD_ST) then OnATTACK_OBJECT_CMD_ST () elseif (MyState == ATTACK_AREA_CMD_ST) then OnATTACK_AREA_CMD_ST () elseif (MyState == PATROL_CMD_ST) then OnPATROL_CMD_ST () elseif (MyState == HOLD_CMD_ST) then OnHOLD_CMD_ST () elseif (MyState == SKILL_OBJECT_CMD_ST) then OnSKILL_OBJECT_CMD_ST () elseif (MyState == SKILL_AREA_CMD_ST) then OnSKILL_AREA_CMD_ST () elseif (MyState == FOLLOW_CMD_ST) then OnFOLLOW_CMD_ST () elseif (MyState == IDLEWALK_ST) then OnIDLEWALK_ST () elseif (MyState == ORBITWALK_ST) then OnORBITWALK_ST() elseif (MyState == REST_ST) then OnREST_ST() elseif (MyState == TANKCHASE_ST) then OnTANKCHASE_ST() elseif (MyState == TANK_ST) then OnTANK_ST() elseif (MyState == PROVOKE_ST) then OnPROVOKE_ST() elseif (MyState == MOVE_CMD_HOLD_ST) then OnMOVE_CMD_HOLD_ST() elseif (MyState == FRIEND_CROSS_ST) then OnFRIEND_CROSS_ST() elseif (MyState == FRIEND_CIRCLE_ST) then OnFRIEND_CIRCLE_ST() else if NewState(MyState)==-1 then TraceAI("Invalid State: "..MyState.." -> IDLE_ST") logappend("AAI_ERROR","MyState set to invalid state: "..MyState) MyState=IDLE_ST end end OnAIEnd() end