• You've discovered RedGuides 📕 an EverQuest multi-boxing community 🛡️🧙🗡️. We want you to play several EQ characters at once, come join us and say hello! 👋
  • IS THIS SITE UGLY? Change the look. To dismiss this notice, click the X --->
Resource icon

Simple Farm Mob Macro (1 Viewer)

Joined
Jan 10, 2018
RedCents
1,231¢
Renamed to Killbot.mac since it's not only great for farming materials you can use it to level newbies etc.

It's a work in progress, It has TONS of comments to help others learn.

Thanks in advance for any Redcents!


killbot.mac
Rich (BB code):
| killbot.mac by Rogue601  v1.4 - work in progress
|
| This macro is meant as a tutorial for myself and possibly others.
| I tried to keep it clean and OVER comment so others know what and why
|
| The point of this macro is to pick a list of mobs you want to farm.
| It's meant to be simple and not have ignorelists or files.
|
| Please don't afk with this and get in trouble.
|
| This requires that you are running MQ2Nav and have a valid mesh.
|
| used with Autoloot this is a very effecting macro for farming tradeskill materials
| I started this to farm bear skins. I've spent more time on it then I did on bearskins. LOL
| enjoy!  

| TODO - Add nav path distance to override radius


| -------------------------------------------------------------------------------------
| Sub Main
| -------------------------------------------------------------------------------------
Sub Main


| COMMENT : Declare variables to save data to work with
| COMMENT : to set a variable use the /varset command example: /varset searchRadius 10000
| COMMENT : to use the variable in code  ${searchRadius}


    /declare debugOn                     bool     outer FALSE    
    /declare farmMobList                     string     outer ${Param1}
    /declare activeMobID                     string    outer 0
    /declare searchRadius                     int     outer ${Param0}    
    /declare searchRadiusZ                     int     outer 500            
    
    
    /if (2 > ${Macro.Params}) {
        /echo 
        /echo Usage: /mac killbot radius mob1, mob2 ... mobx
        /echo
        /echo Example : /mac killbot 10000 cave_bear lion wolf griffon giant        
        /echo Note: There is no anchor, radius is from your current location after a kill.
        /endmac
    }

    | ----------------------------------------------------------------------------
    | farmMobList - Parse the parameters and build a mob list array
    | ----------------------------------------------------------------------------    

    | COMMENT : ${Macro.Params} will tell us how many parameters we received from /mac killbot param0 param2 .. paramX
    | COMMENT : lets build an array of string called findMobList and make it the size of Macro.Params - 1
    | COMMENT : Arrays are a little confusing for new programmers. An Array with 4 elements is accessed by
    | COMMENT : calling arrayname[0],arrayname[1],arrayname[2],arrayname[3]. Since we don't want the searchRadius
    | COMMENT : then we want an array 1 element smaller then the parameter array

    /declare findMobList[${Math.Calc[${Macro.Params}-1]}]      string  outer

    | COMMENT : That's the array with -1 item. Now we need some variable to build a for loop and fill it up
    | COMMENT : with the parameters we send from the /mac killbot 1000 lion tiger bear

      /declare paramCount                               int     local   0
      /declare x                                    int     local   0

    | COMMENT : only run this if more then 1 parameters is sent. param0 is the search radius
    /if (${Macro.Params} > 1) {
        | COMMENT : set our paramCount to the the size of the incoming parameter array - 1
            /varset paramCount ${Math.Calc[${Macro.Params}-1]}
            | COMMENT : loop through the items and put them into our findMobList array
            /for x 1 to ${paramCount}
                /if (${Defined[Param${x}]}) {                                
                    /varset findMobList[${x}] ${Param${x}}
                }
            /next x
    }
        
    | COMMENT : if debug is on we'll display the list
        /if (!${debugOn}) {
        /echo Welcome to Killbot!, Received ${findMobList.Size} mob names to hunt.
        /for x 1 to ${findMobList.Size}
            /echo ${x}. ${findMobList[${x}]}  
        /next x
    }

    
    | ----------------------------------------------------------------------------
    | Initialize - Set some defaults and clear the xtarget list    
    | ----------------------------------------------------------------------------    
    /if (${debugOn}) /echo DEBUG: Initilizing MobList, xTar and Nav Mesh
    /xtar remove
    /nav reload
    
    
    | ----------------------------------------------------------------------------
    | Main Loop - This is where the magic happens. 
    |        1.  Agroed: If we are agroed deal with that first
    |        2. FindMob: Find the type of mob name you are looking for
    |        3.  NavMob: Navigate to the mob using MQ2Nav
    |        4. KillMob: Kill the Mob using MQ2Melee    
    | ----------------------------------------------------------------------------
        :mainloop
                    
            
         | -----------------------------------------------------------------------------------
        | 1. AGROED : If I don't have an activeMobID, but I have an Xtar that means
        |       I have been agroed.  Let's make that my active target and skip the findMob
        |------------------------------------------------------------------------------------
                /if (${activeMobID}==0 && ${Me.XTarget[1].ID}) {
                    
                    /if (${debugOn}) /echo DEBUG: Agroed: activeMobID: ${activeMobID} Xtar: ${Me.XTarget[1].ID}
                    /varset activeMobID ${Me.XTarget[1].ID}
                    
            
        | ----------------------------------------------------------------------
        | 2. FindMob : activeMobID NO then Findone or wait
        |     Here we check for an activeMobID or if we have agro in Xtarget[1]
        |    If we do NOT then we need to call FindMob
        | -------------------------------------------------------------------------
        } else /if (${activeMobID}==0 && !${Me.XTarget[1].ID}) {
                /if (${debugOn}) /echo DEBUG: gotoloopfindmob: No Active Target.
                /varset activeMobID 0
                /call FindMob
                
                
         
        
        | --------------------------------------------------------------------------------
        | 3. NavMob: If we have an activeMobID and it's located further then 30 then Navigate
        |         If we have an Xtar because of agro lets stop nav and switch
        | ---------------------------------------------------------------------------------  
        } else /if (${Spawn[id ${activeMobID}].ID} && ${Spawn[${activeMobID}].Distance} > 30) {
        
            /if (${activeMobID} == ${Me.XTarget[1].ID} || !${Me.XTarget[1].ID}) {
                /call NavMob    
            } else {
                /squelch /nav stop
                /varset activeMobID ${Me.XTarget[1].ID}    
            }
          

        | ----------------------------------------------------------
        | 4. KillMob : If everything worked we are near the activeMobID
        | ----------------------------------------------------------              
        } else {
                
                /call KillMob    

        }
            
            
           | COMMENT : the mainloop runs every 10 seconds. Change this to spead it up or slow it down.
           /doevents
           /delay 10
            
    /goto :mainloop

    :mainend
    /endmac
/return 



| -------------------------------------------------------------------------------------
| OnExit - Things it does when you type /endmac
| -------------------------------------------------------------------------------------
:OnExit
    /setchattitle MQ2
    /squelch /nav stop
    /stick off
    /end
        


| -------------------------------------------------------------------------------------
| Sub FindMob - Locate a Mob in the radius you specified
| -------------------------------------------------------------------------------------
Sub FindMob
    | COMMENT : Simple radius search. This will be replaced with a NAV search eventually
    | REMOVED : old simple one parameter search 
    | REMOVED : /varset activeMobID ${NearestSpawn[npc targetable radius ${searchRadius} zradius ${searchRadiusZ} "${farmMobList}"].ID}

    /declare closestMobID                     int    local 0
    /declare closestMobDistance                float    local 99999
    /declare checkMobID                    int    local 0
    /declare checkMobDistance                float    local 0
    /declare x                        int    local 0    
    
    | COMMENT : Lets find which of our mobs is closest and set it as the activeMobID
    | COMMENT : We set the initial value of closestMobDistance to 99999 to guarantee the first
    | COMMENT : mob checked would be chosen by default.
    | COMMENT : We run a for loop on each mob name in the list and check it against the current one.
    
    /for x 1 to ${findMobList.Size} 
        /varset checkMobID ${NearestSpawn[npc targetable radius ${searchRadius} zradius ${searchRadiusZ} "${findMobList[${x}]}"].ID}
        /varset checkMobDistance ${Spawn[${checkMobID}].Distance}
        /if (${closestMobDistance} > ${checkMobDistance} && ${checkMobID} > 0)    {
            /varset closestMobID ${checkMobID}
            /varset closestMobDistance ${checkMobDistance}
        }
    /if (${debugOn}) /echo DEBUG: FindMob:"${findMobList[${x}]}" closestMobID:${closestMobID} closestMobDistance:${closestMobDistance} checkMobID:${checkMobID} checkMobDistance:${checkMobDistance} 
        
    /next x

    | COMMENT : Only change the activeMobID if we actually found something
    | NOTE : You can do if statements in one line if there is only one command. 
    /if (${closestMobID} > 0) /varset activeMobID ${closestMobID}
    
    /if (${debugOn}) /echo DEBUG: FindMob: radius ${searchRadius} zradius ${searchRadiusZ} "${farmMobList}"
    /if (!${activeMobID}) {
        /setchattitle Scanning for Targets
        /echo FindMob: Nothing Found waiting 5 seconds. 
        /delay 50
    } 
           
/return

| -------------------------------------------------------------------------------------
| Sub NavMob - Navigate using MQ2Nav 
| -------------------------------------------------------------------------------------
Sub NavMob
    | COMMENT : Once activeMobID is set we use MQ2Nav to get there.
    /if (${debugOn}) /echo DEBUG: NavMob: Tracking "${Spawn[${activeMobID}].CleanName}" ID: ${activeMobID} Distance: ${Spawn[${activeMobID}].Distance}
    /setchattitle Tracking ${Spawn[${activeMobID}].CleanName}
    /squelch /nav id ${activeMobID}   | COMMENT: squelch removes the output of the nav spam


    
/return    

| -------------------------------------------------------------------------------------
| Sub KillMob - Target and Kill
| -------------------------------------------------------------------------------------
Sub KillMob
    
    | COMMENT : This is the kill code. It's only reachable if activeMobID is set and it's close range

    /if (${debugOn}) /echo DEBUG: Killbot: Killing a ${Spawn[${activeMobID}].CleanName} ID: ${activeMobID}
        
    /target id ${activeMobID}
    /if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        
        | COMMENT : Turn off Nav and activate MQ2Melee's /stick and /killthis
        /squelch /nav stop
        /stick 8 uw
        /setchattitle Killing ${Target.CleanName}
        /killthis
                
        :gotoloopkeepfighting
        /if (${Target.ID} && ${Me.Combat} && ${Target.Type.Equal[npc]}) {
            /delay 10
            /doevents
            /if (${debugOn}) /echo DEBUG: KillMob: Combat Loop
            /goto :gotoloopkeepfighting            
        } 

    } else { 
        /varset activeMobID 0 
    }
            
    /if (${Target.Type.Equal[corpse]}) {
        /if (${debugOn}) /echo DEBUG: KillMob: Clearing Corpse Target
        /squelch /target clear
        /varset activeMobID 0
    }
    
/return

Updates:
1.4 - Added code to stop nav if you pickup agro and switch to that target
1.3 - Added multi mob parameter handling. type "/mac killbot 10000 lion bear wolf" if will find the closest one and start killing
1.2 - Xtarget is now step 1 in the loop. Works pretty well and cleaned up comments.
1.1 - pulled everything out of the main and put them in subs. added a mainloop and commented the @#$@#$ out of it.



IF you don't know how to make a mesh it's easy.
Just fireup MeshGenerator and setup the folders.
Then load the zone, generate a mesh and save.
use "/plugin mq2nav"
use "/nav reload" if you change it while in the zone.


I'm sure there are better ones I just wanted to learn and it does what I want for now.
 
Last edited:
Simple Farm Mob Macro.

It requires MQ2nav and a mesh.

you just type /farm mobname radius and off it goes.

use "/endmac" to stop it.

You shouldn't afk with this. It's not smartenough to do anything but chainkill :D

farm.mac
Rich (BB code):
#turbo
Sub Main

/echo Usage: /farm mobstring radius, example : /farm cave_bear 300 


:findMob

    /target id ${NearestSpawn[npc targetable radius ${Param1} zradius 100 "${Param0}"].ID}
    :navto
    /if (${Spawn[${Target.ID}].Distance} > 15) {
        /nav id ${Target.ID}
        /delay 10
        /goto :navto
        }
        
    /killthis
    /delay 10
    /goto :findMob

/return



I suggest you use posse too.
Rich (BB code):
/posse on
/posse cmdadd "/endmac"
/posse save

IF you don't know how to make a mesh it's easy.
Just fireup MeshGenerator and setup the folders.
Then load the zone, generate a mesh and save.
use "/plugin mq2nav"
use "/nav reload" if you change it while in the zone.


I'm sure there are better ones I just wanted to learn and it does what I want perfectly.

Thanks in advance for any Redcents!
rogue601

In the spirit of learning. As this macro exists it will target an NPC repeatedly every 10th of a second. Targeting something sends a packet to the EQ servers. Spamming targeting like that is a bad thing.

Rich (BB code):
:findMob
/if (!${Target.ID} && !${Target.Type.Equal[Corpse]} && !${Me.XTarget}) {
    /target id ${NearestSpawn[npc targetable radius ${Param1} zradius 100 "${Param0}"].ID}
}
    :navto
    /if (${Spawn[${Target.ID}].Distance} > 15) {
        /nav id ${Target.ID}
        /delay 10
        /goto :navto
        }
        
    /killthis
    /delay 10
    /goto :findMob

/return

Would reduce significantly the amount of times you target something.

However, targeting outside of your clip range or without line of sight will also notify the EQ servers of potential usage of MQ2. Which is why bigger macros such as KA doesn't target a mob until you get close. Much like you've used ${Spawn[${Target.ID}].Distance} you can also use an ID to get information about a spawn. IE:
Rich (BB code):
/declare myTargetID string outer 0
:findMob
/if (!${myTargetID} && !${Spawn[id ${myTargetID}].Type.Equal[Corpse]} && !${Me.XTarget}) {
         /varset myTargetID ${NearestSpawn[npc targetable radius ${Param1} zradius 100 "${Param0}"].ID}
}

:navto
/if (${Spawn[${myTargetID}].Distance} > 15) {
        /nav id ${myTargetID}
        /delay 10
        /goto :navto
} else /if (!${Target.ID} || ${Target.ID} != ${myTargetID}
        /target id ${myTargetID}
        /delay 10
        
}

/if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /killthis
}
/goto :findMob

Using it that way will find the nearest spawn and assign that as the intended target. It will then navigate to the target and then target it and kill it.

While attempting not to overload you. I followed @joojoobee idea and created a sub to not just pull the nearest mob using Distance in a straight line which gets the hypotenuse of the two XY coordinates, but instead created a method of creating a list of nearby creatures and checking to see if they have a navigation path to them, got the distances of the paths, then compared them all until I found the one with the shortest navigation path.

Rich (BB code):
Sub TargetShortest
    /declare PullTargetID int local 0
    /declare TargetDistance[20] int local 0
    /declare Shortest
    
    /if (!${Me.XTarget[1].ID}) {
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 radius ${PullRange} zradius ${Zrange}]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
            /declare PullList[${MobsInRange},2] int local 0
            /for i 1 to ${MobsInRange}
                /if (${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].ID}]}) {
                        /varset ${PullList[${j},1]} ${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].ID}
                        /varset ${PullList[${j},2]} ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].ID}]}]}
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        /varcalc j ${j}+1
                    }
                }
            /next i
            /varset myTargetID ${PullTargetID}
        }
    }
/return

The above is the jist of it. I realize that this is much more complicated than what you produced. But if you're interested in coding beyond what you've started then it helps to have someone explain things to you, offer code snippets, etc. So in the spawn searches above I've used some different variables for my macro; such as PullRange, and ZRange. I've also added a makeshift ignore list where you can get the macro to ignore mobs that are on alert list 1. After the /next i I added a /varset myTargetID to work with my previous example. So I realize that the jump from my previous examples to this one was quite large. Let me try to put all of this into a single macro setup. I haven't tested any of it to work well together and ensure all variables are correct and defined.

Rich (BB code):
Sub Main
    /echo Usage: /mac farm mobstring radius, example : /mac farm cave_bear 300 


    /declare FarmMob int outer ${Param0}
    /declare PullRange int outer ${Param1}
    /declare ZRadius int outer 100
    /declare myTargetID string outer 0
    
    :findMob
    /if (!${Spawn[id ${myTargetID}].ID} || ${Spawn[id ${myTargetID}].Type.Equal[Corpse]}) {
        /call TargetShortest
    }
    :navto
    /if (${Spawn[${myTargetID}].Distance} > 15) {
         /if (!${Navigation.Active}) /nav id ${myTargetID}
        /delay 10
        /goto :navto
    } else /if (!${Target.ID} || ${Target.ID} != ${myTargetID}) {
        /target id ${myTargetID}
        /delay 10
    }


    /if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /killthis
    }
    /goto :findMob
/return 


Sub TargetShortest
    /declare PullTargetID int local 0
    /declare TargetDistance[20] int local 0
    /declare Shortest int local 0
    
    /if (!${Me.XTarget[1].ID}) {
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
            /declare PullList[${MobsInRange},2] int local 0
            /for i 1 to ${MobsInRange}
                /if (${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        /varset ${PullList[${j},1]} ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset ${PullList[${j},2]} ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        /varcalc j ${j}+1
                    }
                }
            /next i
            /varset myTargetID ${PullTargetID}
        }
    }
/return

The above should work out to improve the efficiency of your macro and reduce the amount of targeting packets being sent and increase the efficiency at which you navigate to targets.
 
You should tell this to the people who made Kissassist. It literally spams target swaps on most characters during combat. Especially healers that have a DPS section enabled. It spends its free time targeting the mob then the tank then the mob then the tank x100

Yeah KA does. It targets to pull stuff. When you use it with nav it targets things around corners and walls etc. I guess that's why I didn't think it was an issue.
 
Yeah KA does. It targets to pull stuff. When you use it with nav it targets things around corners and walls etc. I guess that's why I didn't think it was an issue.

I don't know whether or not to tell you it is.

If chat is right and it sends packets to daybreak (I'm not a l33t coder enough to know if it does or if daybreak would care that it does) - anyone using kissassist is going to send 100x more packets than you. If ANYONE sets off an alarm at daybreak it'll be people using Kissassist all the time (so all of us here) and not you.


I just sat here and watched Kissassist and it changed targets 17 times during an 8 second fight.
 
I tried your code that starts off
Rich (BB code):
/declare myTargetID string outer 0
:findMob
/if (!${myTargetID} && !${Spawn[id ${myTargetID}].Type.Equal[Corpse]} && !${Me.XTarget}) {
         /varset myTargetID ${NearestSpawn[npc targetable radius ${Param1} zradius 100 "${Param0}"].ID}
}
and it crashed my client because i forgot to leave in the Main and /return :D

Then I tried the bigger code you have and it errored out
View attachment 13696

I'm going to change mine to varset the nearest and then nav to it, then kill

- - - Updated - - -

Ok this is your code from above with a main.

there were several minor syntax errors, but this one works as intended.

It defines a targetID variable. Puts the nearest target in that variable. Navigates to it's location every second assuming it's moving.
When it gets in range then it issues the /target and /killthis
I took out the qualifying if at the start because once it ran the first time it was FALSE after that. It doesn't handle agro mobs, but I noticed that when something agroed like a hillgiant the /killthis did eventually trigger. I'll probably add the xtar back

Rich (BB code):
#turbo
Sub Main
 
/echo Usage: /farm mobstring radius, example : /farm cave_bear 300 

/declare myTargetID string outer 0
:findMob

         /varset myTargetID ${NearestSpawn[npc targetable radius ${Param1} zradius 100 "${Param0}"].ID}


:navto
/if (${Spawn[${myTargetID}].Distance} > 15) {
        /nav id ${myTargetID}
        /delay 10
        /goto :navto
} else {
    /if (!${Target.ID} || ${Target.ID} != ${myTargetID}) {
            /target id ${myTargetID}
            /delay 10    
           }
}

/if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /killthis
}
/goto :findMob

/return

- - - Updated - - -

I don't know whether or not to tell you it is.

If chat is right and it sends packets to daybreak (I'm not a l33t coder enough to know if it does or if daybreak would care that it does) - anyone using kissassist is going to send 100x more packets than you. If ANYONE sets off an alarm at daybreak it'll be people using Kissassist all the time (so all of us here) and not you.


I just sat here and watched Kissassist and it changed targets 17 times during an 8 second fight.


Funny thing is while I'm coding this my main group is grinding using kissassist. If I'm gonna be busted I think it would have happened. Although I keep to myself, use posse and don't bother anyone.
 
Funny thing is while I'm coding this my main group is grinding using kissassist. If I'm gonna be busted I think it would have happened. Although I keep to myself, use posse and don't bother anyone.

Fret not we have people doing much worse things than you and they aren't being busted!

Keeping to yourself and using Posse is perfect - you probably don't have anything to ever worry about playing like that.
 
DGB knows who uses mq2. There are countless ways our toons give them to figure it out. One of them is when we target things that we shouldn't be able too or spam them with target packets etc. They just don't care about us using mq2.

That being said, good bots act like actual toons being run by people. Spamming target commands after you have something targeted is bad coding.
 
One of my absolute favorite features of MQ2 is being able to /tar anything on the map. I honestly use it about 25-40x an hour whenever I play... If it's a bad thing I don't wanna be good.
 
Apologies, went shopping and just got in. I can't see the attachment. I've infored Redbot about it and hopefully we can see about getting it sorted. Until then, no attachment for me to see the error unfortunately.

As for targeting. The targeting I was concerned about was targeting things outside of the clipping range by using an arbitrary /target ${Nearest etc etc. Because you don't know how far away the nearest will be. The concern wasn't just with the amount of times you target things so much as targeting things you should be even in the realm of capable of targeting. This is as per eqmule . Targeting things sends a pack. It's bad form to target arbitrary things without any conditions for that targeting range/LOS etc. DBG's standing has more or less been to ignore it, but they do frown upon it heavily when used to target things outside of clipping range. KA does target things around corners (Can by done by manipulating the camera) and things you don't have LoS of, IE: Inside of buildings you aren't currently inside of yet. But it does so once you are within a specific range of the target. DBG can see that you're targeting things using an illegal method, but haven't cared to do anything at this point. Waiting until you have LOS until you target is also an option as an included && included into the conditions for target. ${Spawn[${TargetID}].LineOfSight} as LineOfSight is a member of the spawn dataType. There was a post about targeting the pull mob in your pullroutine being a bad thing. I'm pretty sure it was in some update notes a while back.

Something that apparently hasn't been enforced up to this point.

Rich (BB code):
19 Aug 2015 by EqMule
- Please don't use MQ2 for unattended gameplay (afk botting)
- Updated for LIVE
- This is a heads up:
In the near future I will restrict /target to a radius of 360
I do this because its unreasonable to target stuff across zones.
There is absolutely no scenario where you have a reason to target a mob
until its within that range. So change your macros now if they do this.
If you have line of sight to a mob there will be no radius restriction.

I'm fairly certain there was mention of not targeting until you were at least close to the mob, just can't find it.
 
DGB knows who uses mq2. There are countless ways our toons give them to figure it out. One of them is when we target things that we shouldn't be able too or spam them with target packets etc. They just don't care about us using mq2.

That being said, good bots act like actual toons being run by people. Spamming target commands after you have something targeted is bad coding.

Daybreak is well within the law to scan the Everquest folder too. Mq2 places files in the EQ folder which would give us away immediately.

Honorbuddy recently shut down because Blizzard went so far as to scan the names of each window/program you had active on your computer. (according to Bosslands press release anyway that is googleable)

A year or so before that Archebuddy had to shut down because Archeage decided to scan a computer's processes to see you were running Archebuddy.exe


So - yea there's easier/countless ways for them to sniff us out =P
 
Correction to my varset was made. Tested in game. Works pretty good when I used /mac farm npc 100000 with a zradius hardcoded to 1000. I did get hung up after a few mobs and will look into the issue.

---Updated---
Now handling adds.
&#8203;/ignorethis Will Ignore just the target you have, IE: a_wasp_cocoon_03
/ignorethese Will Ignore all things with that name, IE: a_wasp_cocoon
Swapped the parameters so that you can /mac farm 10000 without a specific mob given.
Usage is now /echo Usage: /mac farm radius mobstring, example : /mac farm 10000 cave_bear

Rich (BB code):
    |||||||||||||||||||||||||||||||||||||||||||||||||||
|Farm.mac by Chatwiththisname
|v1.0 ~ Initial release 2/15/2018
|
|Usage: /mac Farm radius target ~~ /mac farm 400 pyrilen
|        /mac farm radius ~~ /mac farm 1000
|        
|
|Purpose: Will kill and move anything forever in a radius
|            near you. It -WILL- navigate the entire zone.
|            IE: Used in RSS I started at entrance, come back
|            an hour later and I was doing the raid mobs. 
|            
|        If you provide it a target's partial/full name it will 
|        only hunt down those creatures. But it will react to adds.
|
|        /ignorethis to ignore your current target only.
|        /ignorethese to ignore all spawns with your targets full name. 
|
    |||||||||||||||||||||||||||||||||||||||||||||||||||


#bind AddToIgnore
#bind AddThisIgnore


Sub Main(int Param0, string Param1)
    /alias /ignorethis /call Bind_AddThisIgnore
    /alias /ignorethese /call Bind_AddToIgnore
    /declare FarmMob string outer ${Param1}
    /declare PullRange int outer ${Param0}
    /declare ZRadius int outer 1000
    /declare myTargetID string outer 0
    /declare Debugging bool outer FALSE
    /echo Attempting to farm ${FarmMob}.
    /echo Usage: /farm radius mobstring, example : /farm 10000 cave_bear
    
    :findMob
    /if (${Target.Type.Equal[corpse]}) /squelch /target clear
    /if (!${Spawn[id ${myTargetID}].ID} || ${Spawn[id ${myTargetID}].Type.Equal[Corpse]} && !${Me.XTarget[1].ID}) {
        /varset myTargetID 0
        /call TargetShortest
    }
    :navto
    /if (${Spawn[${myTargetID}].Distance} > 30) {
        /squelch /nav id ${myTargetID}
        /delay 10
        /goto :navto
    } else /if (!${Target.ID} && ${Target.ID} != ${myTargetID} && ${myTargetID} != 0) {
        /if (${Debugging}) /echo I'm targeting ${Spawn[${myTargetID}].CleanName} ID: ${myTargetID}
        /target id ${myTargetID}
        /delay 10
    }


    /if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /squelch /nav stop
        /stick 8 uw loose
        /setchattitle Killing ${Target.CleanName}
        /killthis
        :waitTillDead
        /if (${Target.ID} && ${Me.Combat} && ${Target.Type.Equal[npc]}) {
            /delay 10
            /goto :waitTillDead
        } else /if (${Target.Type.Equal[corpse]}) {
            /target clear
            /varset myTargetID 0
        }
    } else /if (${Me.XTarget[1].ID}) {
        /if (${Spawn[npc radius 50 zradius 50].ID}) {
            /target id ${Spawn[npc radius 50 zradius 50].ID}
            /setchattitle Handling add, ${Spawn[npc radius 50 zradius 50].CleanName}
        } else /if (${Spawn[id ${Me.XTarget[1].ID}].Distance} > 30) {
            /nav id ${Me.XTarget[1].ID}
            /setchattitle Navigating to add ${Spawn[id ${Me.XTarget[1].ID}].CleanName}
            :nav2stray
            /if (${Spawn[${Me.XTarget[1].ID}].Distance} > 30) {
                /if (!${Navigation.Active}) /nav id ${Me.XTarget[1].ID}
                /delay 10
                /goto :nav2stray
            }
        }
        /killthis
        /setchattitle Killing ${Target.CleanName}
        /goto :waitTillDead
    }
    /goto :findMob
/return
    :OnExit
    /setchattitle MQ2
    /end


Sub TargetShortest
    /declare PullTargetID int local 0
    /declare TargetDistance[20] int local 0
    /declare Shortest int local 0
    
    /if (!${Me.XTarget[1].ID}) {
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
            /if (${Debugging}) /echo There were mobs ${MobsInRange} in range. 
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
            /declare PullList[${MobsInRange},2] int local 0
            /for i 1 to ${MobsInRange}
                /if (${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        /varset PullList[${j},1] ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset PullList[${j},2] ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        /varcalc j ${j}+1
                    }
                }
            /next i
            /varset myTargetID ${PullTargetID}
            /setchattitle Going to kill ${Spawn[id ${myTargetID}].CleanName}!
        }
    }
/return


Sub Bind_AddToIgnore
    /alert add 1 ${Target.CleanName}
    /varset myTargetID 0
    /squelch /target clear
/return 


Sub Bind_AddThisIgnore
    /alert add 1 ${Target.Name}
    /varset myTargetID 0
    /squelch /target clear
/return
 
Last edited:
Been running in a zone bymyself farming phat lewt (pp drops lol) non-stop for about an hour now with my last posted code. Try it out, lemme know what ya think.
 
Updated post #12, while before it supported alert list 1 ignoring of mobs, it required it was set prior to the macro being ran or it would get hung up on the mob unless you knew to /varset myTargetID to 0. I've automated that process, you can now just type

&#8203;/ignorethis Will Ignore just the target you have, IE: a_wasp_cocoon_03
/ignorethese Will Ignore all things with that name, IE: a_wasp_cocoon
 
Last edited:
haha, we programmer do get carried away don't we :D

kissassist hunter wasn't working in the cave in creseant reach so i threw that together quick to farm skins for tailoring.

Now I need something to farm a list

I orginally had the radius as the first parameter, but decided the what was more important to me then the were.

I personally want to avoid doing the ignore option. it's too much work to weed out every npc in each zone.

I'm using your macro right now. It's working great.

What I'd like is to split para1 into an array and hunt all of it. I can do the for x loop. I just need to find out what the split command is.

/farm 1000 lion,spider,wolf
 
You don't need to split it, you just need to know how many parameters are mob types. ie your /mac farm 1000 lion spider wolf would mean your macro has 4 ${Macro.Params} , and you know from your macro setup that the first is always a radius (at least it's meant to be, so if it isn't your macro should end) so then you know you have 3 params to declare an array size and what to declare in them
 
You don't need to split it, you just need to know how many parameters are mob types. ie your /mac farm 1000 lion spider wolf would mean your macro has 4 ${Macro.Params} , and you know from your macro setup that the first is always a radius (at least it's meant to be, so if it isn't your macro should end) so then you know you have 3 params to declare an array size and what to declare in them

in that case then yeah I'd definitely leave the radius first. I'm gonna try it out. thanks. I guess I hit my thanks limit for the day? I'll hit you up when i get more :D

- - - Updated - - -

Correction to my varset was made. Tested in game. Works pretty good when I used /mac farm npc 100000 with a zradius hardcoded to 1000. I did get hung up after a few mobs and will look into the issue.

---Updated---
Now handling adds.
&#8203;/ignorethis Will Ignore just the target you have, IE: a_wasp_cocoon_03
/ignorethese Will Ignore all things with that name, IE: a_wasp_cocoon
Swapped the parameters so that you can /mac farm 10000 without a specific mob given.
Usage is now /echo Usage: /mac farm radius mobstring, example : /mac farm 10000 cave_bear

Rich (BB code):
    |||||||||||||||||||||||||||||||||||||||||||||||||||
|Farm.mac by Chatwiththisname
|v1.0 ~ Initial release 2/15/2018
|
|Usage: /mac Farm radius target ~~ /mac farm pyrilen
|        /mac farm radius ~~ /mac farm 1000
|        
|
|Purpose: Will kill and move anything forever in a radius
|            near you. It -WILL- navigate the entire zone.
|            IE: Used in RSS I started at entrance, come back
|            an hour later and I was doing the raid mobs. 
|            
|        If you provide it a target's partial/full name it will 
|        only hunt down those creatures. But it will react to adds.
|
|        /ignorethis to ignore your current target only.
|        /ignorethese to ignore all spawns with your targets full name. 
|
    |||||||||||||||||||||||||||||||||||||||||||||||||||


#bind AddToIgnore
#bind AddThisIgnore


Sub Main(int Param0, string Param1)
    /alias /ignorethis /call Bind_AddThisIgnore
    /alias /ignorethese /call Bind_AddToIgnore
    /declare FarmMob string outer ${Param1}
    /declare PullRange int outer ${Param0}
    /declare ZRadius int outer 1000
    /declare myTargetID string outer 0
    /declare Debugging bool outer FALSE
    /echo Attempting to farm ${FarmMob}.
    /echo Usage: /farm radius mobstring, example : /farm 10000 cave_bear
    
    :findMob
    /if (${Target.Type.Equal[corpse]}) /squelch /target clear
    /if (!${Spawn[id ${myTargetID}].ID} || ${Spawn[id ${myTargetID}].Type.Equal[Corpse]} && !${Me.XTarget[1].ID}) {
        /varset myTargetID 0
        /call TargetShortest
    }
    :navto
    /if (${Spawn[${myTargetID}].Distance} > 30) {
        /squelch /nav id ${myTargetID}
        /delay 10
        /goto :navto
    } else /if (!${Target.ID} && ${Target.ID} != ${myTargetID} && ${myTargetID} != 0) {
        /if (${Debugging}) /echo I'm targeting ${Spawn[${myTargetID}].CleanName} ID: ${myTargetID}
        /target id ${myTargetID}
        /delay 10
    }


    /if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /squelch /nav stop
        /stick 8 uw loose
        /setchattitle Killing ${Target.CleanName}
        /killthis
        :waitTillDead
        /if (${Target.ID} && ${Me.Combat} && ${Target.Type.Equal[npc]}) {
            /delay 10
            /goto :waitTillDead
        } else /if (${Target.Type.Equal[corpse]}) {
            /target clear
            /varset myTargetID 0
        }
    } else /if (${Me.XTarget[1].ID}) {
        /if (${Spawn[npc radius 50 zradius 50].ID}) {
            /target id ${Spawn[npc radius 50 zradius 50].ID}
            /setchattitle Handling add, ${Spawn[npc radius 50 zradius 50].CleanName}
        } else /if (${Spawn[id ${Me.XTarget[1].ID}].Distance} > 30) {
            /nav id ${Me.XTarget[1].ID}
            /setchattitle Navigating to add ${Spawn[id ${Me.XTarget[1].ID}].CleanName}
            :nav2stray
            /if (${Spawn[${Me.XTarget[1].ID}].Distance} > 30) {
                /if (!${Navigation.Active}) /nav id ${Me.XTarget[1].ID}
                /delay 10
                /goto :nav2stray
            }
        }
        /killthis
        /setchattitle Killing ${Target.CleanName}
        /goto :waitTillDead
    }
    /goto :findMob
/return
    :OnExit
    /setchattitle MQ2
    /end


Sub TargetShortest
    /declare PullTargetID int local 0
    /declare TargetDistance[20] int local 0
    /declare Shortest int local 0
    
    /if (!${Me.XTarget[1].ID}) {
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
            /if (${Debugging}) /echo There were mobs ${MobsInRange} in range. 
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
            /declare PullList[${MobsInRange},2] int local 0
            /for i 1 to ${MobsInRange}
                /if (${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        /varset PullList[${j},1] ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset PullList[${j},2] ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        /varcalc j ${j}+1
                    }
                }
            /next i
            /varset myTargetID ${PullTargetID}
            /setchattitle Going to kill ${Spawn[id ${myTargetID}].CleanName}!
        }
    }
/return


Sub Bind_AddToIgnore
    /alert add 1 ${Target.CleanName}
    /varset myTargetID 0
    /squelch /target clear
/return 


Sub Bind_AddThisIgnore
    /alert add 1 ${Target.Name}
    /varset myTargetID 0
    /squelch /target clear
/return

I'm using this atm and it works good.

I'm confused about why your getting a mob count and then looping.
Why not just goto the nearest one? Seems like there's no need for arrays and loops etc.

Im not 100% clear on what it all does.
It looks like your making a list, then checking to make sure the nav path exists? What does it do if there isn't a path? and if it doesn't nothing then why check for the path at all?

I'll have to go check MQ2 wiki I don't know what some of that stuff even does. /squelch /alert

I like what you did with the chat box title.
 
The loop looks for all the relevant mobs in range which is ${MobsInRange} and the integer is from the spawncount search "${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}"

The loop also find the pathlength for each of the MobsInRange, so which it can calculate which is going to be the shortest run to.

The end of each loop it sets myTargetID as which ever is shorter path, then when all loops are run sets the PullTargetID as that (the shortest to run to)

I feel like the PathLength varset could be made shorter though since your searching for the spawn ID, even though you have it already searched and have the ID ? also with mob movement, i feel like using a new search for the ID in it could result in a miscalc... not that it would mean much since the path lengths would be close enough to not care about for a simple farm macro...
 
The code can be stuck in an infinite loop if the closest npc can't be navigate too. Otherwise it looks pretty solid. The targetshortest will happen quick enough that npc movement can be ignored.
 
Here I've added comments to the code so that I can hopefully help you understand it better @rogue601


Rich (BB code):
Sub TargetShortest
    /declare PullTargetID int local 0
    /declare Shortest int local 0
    
    /if (!${Me.XTarget[1].ID}) {
        |In order to see if I should even loop through to see how many mobs are in range I need to get a count based on my conditions
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
            /if (${Debugging}) /echo There were mobs ${MobsInRange} in range.
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
        |I created an array and made it the size of the mobcount by the 2 to store each mob's ID and the length of their nav path
            /declare PullList[${MobsInRange},2] int local 0
            |I set i equal to 1 and I iterate through each mob
            /for i 1 to ${MobsInRange}
                |just in case something dies, I don't to result in NULL during my check producing results
                /if (${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    |If there is a path and only if there is a path will I enter the following block statement. This is done to avoid adding mobs to the array that don't have a path.
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        |Now that I know this mob has a Navigation path, I need to add it to the array where ${j} is incremented only if I add a mob's ID and Path Length
                        /varset PullList[${j},1] ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset PullList[${j},2] ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        |If this is the first mob I've added to the array, it is now my target and it has the shortest path. 
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            |Otherwise if the mob I added has a PathLength shorter that the current shortest Nav Path, make it my target and set it as the shortest.
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        |Since I added a mob I need to increment j by 1 in the PullList Array. 
                        /varcalc j ${j}+1
                    }
                }
            |Check the next mob in the NearestSpawn meeting my conditions loop 
            /next i
            |Now that I've exited the loop, the PullTargetID variable is the one I want to navigate to and kill. 
            /varset myTargetID ${PullTargetID}
            |Set the chattitle of the MQ2 window to the macro's status (Suggestion by Kaen01)
            /setchattitle Going to kill ${Spawn[id ${myTargetID}].CleanName}!
        }
    }
/return

Beyond that. To understand a need to loop and find a shortest nav path. I'm standing against the outside wall of a building and directly on the other side of the wall is a mob but the entrance to the building is on the other side of the building, and in front of me is a mob just 5 units farther away. Using Spawn's distance as it's built into MQ2 calculates distance based on straight line distance. While navigation calculates distance based on the length of the path in which you would have to navigate to arrive at the spawn. If you run it with just Spawn[].Distance the macro would want to kill the mob inside the building. With my code it checks their navigation paths and sees the path length for the mob just on the other side of the wall is much farther than the mob in front of me. It compares their navigation path distances and decides to kill the one with the shorter navigational path.

To understand the need to check and see if a path exists. When you create a mesh you have a setting called "Radius" that will -not- draw the mesh directly up against objects or walls (so that your character doesn't run into them) and occasionally a creature can be found in those locations where there is no path existing. Or if there is a cliff or some other type of issue that "breaks" the mesh connections where you cannot navigate to the mob it shouldn't even consider the mob.

So the reason is does nothing if the path doesn't exist is because I can't type /nav id thatTargetsID because I will just generate an error that it couldn't navigate to the location. It would then be stuck trying to nav to that mob in a loop ":navto" repeatedly trying to navigate to the NPC.

The only case where I see an infinite loop @plure is if there are no mobs within the conditions found, in which case it would hang out waiting for mobs to be in range checking for that over and over again until one was in range. Since I don't enter the for loop unless there are ${MobsInRange} and I don't do anything with the ${j} increments unless a mob has a path that exists I can't see where the infinite loop would occur. Could you expand on why you feel there could be an infinite loop?

In the case of binds I feel that would need explaining too. I've created "Binds" and "Sub Bind_AddToIgnore" etc because if I would have set them as basic Sub AddToIgnore without creating a bind, if the alias /ignorethis or /ignorethese is /call'ed then it would result in a subroutine ran into another subroutine overflow. By creating them as binds they work like an "event" which would typically be called by doing /doevents. But then the program would have to wait until /doevents was called and the user would have to /echo ignorethis instead. This allows me to create an alias that calls a sub routine without causing the macro to error, and creates an alias that can do an entire routine. I could have created the alias and used "/multiline ; /alert add 1 ${Target.CleanName} ; /varset myTargetID 0 ; /squelch /target clear" to produce the same results. But I feel this is neater code.

As for /squelch. Squelch is something that says to not output information to the MQ2 window. When you issue a command such as /nav id ${Target.ID} it will get MQ2Nav to output "Navigating to y x z coordinates" etc. Or in the case of /target clear it would output "Target cleared" in purple to the MQ2 Window. In hopes of reducing the amount of spam generated by the macro I'm /sqelch'ing the command so that it doesn't report what I already know it should be doing.

Also, it should be noted that the macro is still "dumb" in that it just attacks the nearest one. It shouldn't be used in a zone with other people because it -will- attack another groups mob because it doesn't check for TargetOfTarget or Aggro on someone in anyway shape or form. With that said, use your brain, sit at your keyboard, monitor what it's doing, and ffs use posse :-)

~Chat
 
Last edited:
I'm gonna continue to work on my own mac just for the experience.
I posted my latest save at the top.
I do want to learn all the stuff you have.


I fine tuned it so now it only does one thing at a time and not a constant loop spam.
It's pretty clean. I have lots to do still.
I'm going to add a loop in FindMob sub to handle multiple mobs as soon as I get back.

/killbot 10000 lion wolf bear spider
 
Rich (BB code):
             /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        |Now that I know this mob has a Navigation path, I need to add it to the array where ${j} is incremented only if I add a mob's ID and Path Length
                        /varset PullList[${j},1] ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset PullList[${j},2] ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        |If this is the first mob I've added to the array, it is now my target and it has the shortest path. 
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            |Otherwise if the mob I added has a PathLength shorter that the current shortest Nav Path, make it my target and set it as the shortest.
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        |Since I added a mob I need to increment j by 1 in the PullList Array. 
                        /varcalc j ${j}+1
                    }

You start off with Shortest/PullTargetID set to 0. You set those for the 1st npc only if you can navigate to that npc, if you can't the Shortest will still be 0 and you will never find a npc that has a smaller nav path length. The solution is to start Shortest = 999999 or something equally as large.
 
But I know I can navigate to that target because I only set the first one to the path length if the navigation path exists in the first place according to the following block if statement which encompasses that section of the code.

Rich (BB code):
/if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {

Since I only ever enter the /varset's for PullList[] and ${j} checks if the path exists there is no case where it will ever set the PullTargetID/Distance to a value of a path that doesn't exist. J is declared and initialized as 1, it is a local variable meaning every time I enter the Sub Routine it is declared and every time I exit the sub routine it is deleted. So the first path that actually exists is the one that I assign to my initial target. All paths that exists following that are compared to the target with the shortest nav path. Thus, there is no case where the nav path would 0 unless the mob is right on top of me, in which case I would be able to kill it. In the case where no navigation path exists for ${MobsInRange} I simply set myTargetID to 0 because I declare an initialize PullTargetID as 0. If myTargetID is 0, then it's assumed I don't have a target becuase I check to see if the spawn exists using ${Spawn[id ${myTargetID}]} to get information, if none of the conditions are met and I don't have any adds it just loops back to the start of the macro where it finds mob again.
 
Ahh Yes, initially when I created that sub routine I had set it to i and had not factored that situation in. I've rewrote this subs a few times and feel that this is the best it can be short of generating an array that is sorted as an array. But to use it in that way for this macro I would have to generate a "map" to follow where I would declare the closest mob, then calculate all the other mobs distance from that mobs current location and set the next one to the second mob, then calculate from the second mob to the third shortest distance etc. But mobs move so I've opted to convert any "hunter" style killemall macros to the find nearest nav and then go kill it, then find the next. Makes it run quick enough that you shouldn't notice any delay of any significant value as long as you keep your overall radius down. I did try this macro in a large zone with the radius set to 10,000 and it made everything run at like 1 FPS for about 8 seconds until it calculated the shortest nav path between every mob in the zone lol. But it still does it. Just wouldn't recommend making that mistake on a low end computer, would likely run you down to 1 FPS for a few minutes.

At least our conversation helped further explain more of the code :-)
 
Hmm I can see where that would be useful, IE: Zones where meshes are jacked up like Velks where it will have a broken mesh but still have found a NavigationPath that exists. IE: If used at the entrance of velks my routine would navigate about 10 units and stop trying to get to the target on the floor above. Because the mesh is broken on the path going up instead of saying no path exists it generates a path that navigates you to the location below/above the target in question. Very valid point. I would have to look into that for sure. That might be a great idea to get it to work more efficiently (at all lol) in a zone like velks.

I'm thinking however as opposed to stopping the loop that I just don't consider that particular mob by adding it as part of the PathExists check.

Rich (BB code):
/if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]} && ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Distance3D} !> ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]})

Or something to that rough effect.
 
Updated to 1.3 added multiple mob options.

/mac killbot 1000 lion wolf spider giant

It works great in wide open places like karana
I like how it's working out.

I can see someone starting a new toon in Cresent Reach leveling fast by just changing the moblist for each level
10000 grove
10000 bear spider
10000 drake
10000 cave_bear
etc.
of course it doesn't cast spells so it's not KA :D


I'll look into the nav pathing next week.

Thanks.
 
I'm currently in plugin training with the creator of some high end plugins. We're actually using this as a tool for me to learn, we're converting the code I shared above into a plugin so I can learn and convert something I've coded at the same time. Not expecting results anytime soon, but It's in the works.
 

Attachments

  • MQ2Farm.png
    MQ2Farm.png
    122.3 KB · Views: 32
Very cool. I use to be a professional coder back in the 90s. I'm enjoying it again.
the syntax is a little odd, but easy enough.
When I saw a goto statement I did a literal LOL.
I haven't seen a goto since I was in highschool in the 80s lol.

I'm not sure how much further I'll take my little project. KA does just about everything I need.
I just couldn't get the hunter feature to work well in those lowbie bear caves.
 
Well, typically when you're farming items for yourself you wouldn't really need to check for health and mana assuming you're in a low level zone. But, it's something that you could consider. I had one of my old macros that I just dug out and went through the code I had for it. I removed goto statements and replaced them with while statement for my checks.

My subs were originally designed with the group in mind, but also, it was intended for use with a bot plugin that I wanted my macro to be used in conjunction with. They've since added pulling to it, but in the mean time I still have my code which I used and have now updated even.

GroupHealthChk
GroupManaChk
GroupDeathChk

are the three subs in question here. I feel at this point you should be okay with reading the majority of this and understanding what it does.

For starters, below are the new variables

Rich (BB code):
    /declare HealAt int outer 100
    /declare HealTill int outer 70
    /declare MedAt int outer 30
    /declare MedTill int outer 100
    /declare UseEQBC bool outer TRUE

And below are the Subroutines I added to the code.


Rich (BB code):
| --------------------------------------------------------------------------------------------
| SUB: GroupManaChk
| --------------------------------------------------------------------------------------------
Sub GroupManaChk
    /if (${Me.XTarget[1].ID}) /return
    /if (!${Me.Combat}) {
        /setchattitle "Group Mana Check"
        /if (${Me.PctMana} < ${MedAt} && ${Me.Class.CanCast}) {
            /echo \arYOU are low on mana!
            /setchattitle "Waiting on YOUR mana to reach ${MedTill}%"
            /if (!${Me.XTarget[1].ID}) {
                /while (${Me.PctMana} <= ${MedTill} && !${Me.XTarget[1].ID}) {
                    /if (${Me.Standing} && !${Me.Casting.ID} && !${Me.Mount.ID}) /sit
                    /delay 10
                }
            }
        }
        /if (${Group}) {
            /declare i int local
            /for i 1 to ${Group}
                /if ((${Group.Member[${i}].PctMana} < ${Med}) && (${Group.Member[${i}].Class.CanCast})) {
                    /echo \ar${Group.Member[${i}].Name} is low on mana!
                    /setchattitle "Waiting on ${Group.Member[${i}].Name}'s mana to reach ${MedTill}%"
                    /if (!${Me.XTarget[1].ID}) {
                        /while (${Group.Member[${i}].PctMana} <= ${MedTill} && !${Me.XTarget[1].ID}) {
                            /delay 10
                        }
                    }
                }
            /next i
        }
    }
/return


| --------------------------------------------------------------------------------------------
| SUB: GroupHealthChk
| --------------------------------------------------------------------------------------------
Sub GroupHealthChk
    /if (${Me.XTarget[1].ID}) /return
    /setchattitle "GroupHealthCheck"
    /if (!${Me.Combat}) {
        /if (${Me.PctHPs} < ${HealTill}) {
            /echo YOU are low on Health!
            /setchattitle "Waiting on YOUR health to reach ${HealTill}%"
            /if (!${Me.XTarget[1].ID}) {
                /while (${Me.PctHPs} <= ${HealAt} && !${Me.XTarget[1].ID}) {
                    /if ((${Me.Standing}) && (!${Me.Casting.ID}) && (!${Me.Mount.ID})) /sit
                    /delay 10
                }
            }
        }
        /if (${Group}) {
            /declare i int local
            /for i 1 to ${Group}
                /if (${Group.Member[${i}].ID}) {
                    /if (${Group.Member[${i}].PctHPs} < ${HealTill}) {
                        /echo ${Group.Member[${i}].Name} is low on Health!
                        /setchattitle "Waiting on ${Group.Member[${i}].Name} health to reach ${HealTill}%"
                        /if (!${Me.XTarget[1].ID}) {
                            
                            /while (${Group.Member[${i}].PctHPs} <= ${HealAt} && !${Me.XTarget[1].ID}) {
                                /if ((${Me.Standing}) && (!${Me.Casting.ID}) && (!${Me.Mount.ID})) /sit
                                /if (${UseEQBC}) {
                                    /declare j int local
                                    /for j to ${Group}
                                        /if (${Group.Member[${j}].State.Equal[Stand]} && !${Group.Member[${j}].Type.Equal[Mercenary]}) /bct ${Group.Member[${j}].Name} //sit
                                    /next j
                                }
                                /delay 10
                            }
                        }
                    }
                }
            /next i
        }
    }
/return


| --------------------------------------------------------------------------------------------
| SUB: GroupDeathChk
| --------------------------------------------------------------------------------------------
Sub GroupDeathChk
    /if (${Me.State.Equal[DEAD]} ) {
        /echo \arYOU~ have died! Waiting for YOU to get off your face.
        /setchattitle "You died, waiting for rez!"
        /while (${Me.STATE.Equal[DEAD]} ) {
            /delay 10


        }
    }
    /if (${Me.XTarget[1].ID}) /return
    /if (${Group}) {
        /declare i int local
        /for i 1 to ${Group}
            /if (${Group.Member[${i}].State.Equal[DEAD]} ) {
                /echo ${Group.Member[${i}].Name} has died. Waiting for them to get off their face.
                /setchattitle "${Group.Member[${i}].Name} has died. Waiting for Rez"
                /if (!${Me.XTarget[1].ID}) {
                    /while (${Group.Member[${i}].State.Equal[DEAD]} && !${Me.XTarget[1].ID}) {
                        /if ((${Me.Standing}) && (!${Me.Casting.ID}) && (!${Me.Mount.ID})) /sit
                        /if (${UseEQBC}) {
                            /declare j int local
                            /for j to ${Group}
                                /if (${j} != ${i}) {
                                    /if (${Group.Member[${j}].State.Equal[Stand]} && !${Group.Member[${j}].Type.Equal[Mercenary]}) /bct ${Group.Member[${i}].Name} //sit
                                }
                            /next j
                        }
                        /delay 10
                    }
                }
            }
        /next i
    }
/return


So that is a lot to take in. But basically the Heal and Mana checks do the same thing as the other but one for mana and one for health. Then the DeathChk checks for group deaths.

I can then add these after my check to clear my target of any corpses

Rich (BB code):
:findMob
    /if (${Target.Type.Equal[corpse]}) /squelch /target clear

But I don't want to Check health mana or for deaths unless I'm not in combat/have aggro.

So I'll wrap it in a check for an XTarget
Rich (BB code):
/if (!${Me.XTarget[1].ID}) {
        /call GroupDeathChk
        /call GroupHealthChk
        /call GroupManaChk
    }

If you aren't a fan of XTarget[1] checking because you store things on your XTarget window such as the MT or MA for easy clicking (not that they aren't in the group window?) Then you would have to instead convert it to check more extensively. Basically a for loop that iterates through the XTarget to check to see if they are Auto Haters before it decides not to do something based on you having an XTarget.

Rich (BB code):
Sub XTargetCheck
    /declare i int local
    /for i 1 to ${Me.XTarget}
        /if (${Me.XTarget[${i}].TargetType.Equal[Auto Hater]}) /return TRUE
    /next i
/return FALSE

When you use a Sub Routine to return information, you have two ways to access that information.

You can use ${Macro.Return} or you can use it as a function.

To use a sub routine as a function you call it as
Rich (BB code):
${XTargetCheck[]}
where XTargetCheck is the name of the routine, and the [] identifies it as a routine, if your subroutine had any parameters, you would pass them inside of the [] using
Rich (BB code):
${XTargetCheck[${Me.PctHPs},${Me.PctMana}]}
for example. that passes your hps and mana as variables.
Warning!!!
You CANNOT use a sub routine as a function in an if/else statement multiple times.

IE: I cannot do the following.

Rich (BB code):
/if (${XTargetCheck[]}) {
    /dosomethinghere
} else /if (!${XTargetCheck[]}) {
    /dosomethingelsehere
}

This was brought up to both the original coder of subs as functions and to EQMule and while there was a shady workaround in place, it's poor coding technique and puts brackets out of balance and that bothers me. If you used the above /if else statement it used to crash you. EQmule said he fixed the CTD but it still didn't work properly after he worked on it extensively and decided that you just needed to use separate if statement, or use ${Macro.Return} to get the information returned by the sub routine.

So now that we have a sub that checks XTarget for a valid aggro mob.

Rich (BB code):
/call XTargetCheck
    /if (!${Macro.Return.Equal[TRUE]}) {
        /call GroupDeathChk
        /call GroupHealthChk
        /call GroupManaChk
    }

Should be a good way to determine if you should enter those subs.

But it would also require that any portion of those subs that checks for an XTarget also be changed to /call XTargetCheck and the !${Macro.Return.Equal[TRUE]}

The alternate method, using the Subroutine as a function would be

Rich (BB code):
/if (!${XTargetCheck[]}) {
        /call GroupDeathChk
        /call GroupHealthChk
        /call GroupManaChk
    }


Where inside the sub routines you would change all checks for ${Me.XTarget[1].ID} to ${XTargetCheck[]}

I suppose I can discuss formatting any /echo's to be more versatile to the eyes. I'm talking about colors. You can make your echo's output colors by using switch statements. \ar (red) \ay (yellow) \ag (green) etc. You could memorize these....../echo \arYou Can't Do That!!!\aw-->\agBut You Can Do This!

Or you could have them defined in your macro.

Rich (BB code):
    /declare Black         string outer \ab
    /declare Blue       string outer \au
    /declare Brown      string outer \a-o
    /declare Cyan       string outer \at
    /declare Gray        string outer \a-w
    /declare Green        string outer \ag
    /declare Pink       string outer \am
    /declare Orange       string outer \ao
    /declare Purple       string outer \ap
    /declare Red           string outer \ar
    /declare Teal       string outer \at
    /declare White       string outer \aw
    /declare Yellow      string outer \ay
    /declare DarkGreen     string outer \a-g
    /declare DarkMaroon    string outer \a-m
    /declare DarkPurple    string outer \a-p
    /declare DarkRed    string outer \a-r
    /declare DarkCyan    string outer \a-t
    /declare DarkBlue    string outer \a-u
    /declare DarkYellow    string outer \a-y

/echo ${Red}You Can't Do That!!!${White}-->${Green}But You Can Do This!

Okay, hopefully that's enough /professoring today for me. If you used to program professionally then you're likely eating this stuff up. :-)
 
Last edited:
Correction to my varset was made. Tested in game. Works pretty good when I used /mac farm npc 100000 with a zradius hardcoded to 1000. I did get hung up after a few mobs and will look into the issue.

---Updated---
Now handling adds.
&#8203;/ignorethis Will Ignore just the target you have, IE: a_wasp_cocoon_03
/ignorethese Will Ignore all things with that name, IE: a_wasp_cocoon
Swapped the parameters so that you can /mac farm 10000 without a specific mob given.
Usage is now /echo Usage: /mac farm radius mobstring, example : /mac farm 10000 cave_bear

Rich (BB code):
    |||||||||||||||||||||||||||||||||||||||||||||||||||
|Farm.mac by Chatwiththisname
|v1.0 ~ Initial release 2/15/2018
|
|Usage: /mac Farm radius target ~~ /mac farm 400 pyrilen
|        /mac farm radius ~~ /mac farm 1000
|        
|
|Purpose: Will kill and move anything forever in a radius
|            near you. It -WILL- navigate the entire zone.
|            IE: Used in RSS I started at entrance, come back
|            an hour later and I was doing the raid mobs. 
|            
|        If you provide it a target's partial/full name it will 
|        only hunt down those creatures. But it will react to adds.
|
|        /ignorethis to ignore your current target only.
|        /ignorethese to ignore all spawns with your targets full name. 
|
    |||||||||||||||||||||||||||||||||||||||||||||||||||


#bind AddToIgnore
#bind AddThisIgnore


Sub Main(int Param0, string Param1)
    /alias /ignorethis /call Bind_AddThisIgnore
    /alias /ignorethese /call Bind_AddToIgnore
    /declare FarmMob string outer ${Param1}
    /declare PullRange int outer ${Param0}
    /declare ZRadius int outer 1000
    /declare myTargetID string outer 0
    /declare Debugging bool outer FALSE
    /echo Attempting to farm ${FarmMob}.
    /echo Usage: /farm radius mobstring, example : /farm 10000 cave_bear
    
    :findMob
    /if (${Target.Type.Equal[corpse]}) /squelch /target clear
    /if (!${Spawn[id ${myTargetID}].ID} || ${Spawn[id ${myTargetID}].Type.Equal[Corpse]} && !${Me.XTarget[1].ID}) {
        /varset myTargetID 0
        /call TargetShortest
    }
    :navto
    /if (${Spawn[${myTargetID}].Distance} > 30) {
        /squelch /nav id ${myTargetID}
        /delay 10
        /goto :navto
    } else /if (!${Target.ID} && ${Target.ID} != ${myTargetID} && ${myTargetID} != 0) {
        /if (${Debugging}) /echo I'm targeting ${Spawn[${myTargetID}].CleanName} ID: ${myTargetID}
        /target id ${myTargetID}
        /delay 10
    }


    /if (${Target.ID} && ${Target.Type.Equal[NPC]}) {
        /squelch /nav stop
        /stick 8 uw loose
        /setchattitle Killing ${Target.CleanName}
        /killthis
        :waitTillDead
        /if (${Target.ID} && ${Me.Combat} && ${Target.Type.Equal[npc]}) {
            /delay 10
            /goto :waitTillDead
        } else /if (${Target.Type.Equal[corpse]}) {
            /target clear
            /varset myTargetID 0
        }
    } else /if (${Me.XTarget[1].ID}) {
        /if (${Spawn[npc radius 50 zradius 50].ID}) {
            /target id ${Spawn[npc radius 50 zradius 50].ID}
            /setchattitle Handling add, ${Spawn[npc radius 50 zradius 50].CleanName}
        } else /if (${Spawn[id ${Me.XTarget[1].ID}].Distance} > 30) {
            /nav id ${Me.XTarget[1].ID}
            /setchattitle Navigating to add ${Spawn[id ${Me.XTarget[1].ID}].CleanName}
            :nav2stray
            /if (${Spawn[${Me.XTarget[1].ID}].Distance} > 30) {
                /if (!${Navigation.Active}) /nav id ${Me.XTarget[1].ID}
                /delay 10
                /goto :nav2stray
            }
        }
        /killthis
        /setchattitle Killing ${Target.CleanName}
        /goto :waitTillDead
    }
    /goto :findMob
/return
    :OnExit
    /setchattitle MQ2
    /end


Sub TargetShortest
    /declare PullTargetID int local 0
    /declare TargetDistance[20] int local 0
    /declare Shortest int local 0
    
    /if (!${Me.XTarget[1].ID}) {
        /declare MobsInRange int local ${SpawnCount[npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"]}
        /declare i int local 0
        /declare j int local 1
        /if (${MobsInRange}) {
            /if (${Debugging}) /echo There were mobs ${MobsInRange} in range. 
        |** PullList[#,1] = ID of mob, PullList[#,2] = PathLength **|
            /declare PullList[${MobsInRange},2] int local 0
            /for i 1 to ${MobsInRange}
                /if (${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].Name.NotEqual[NULL]} && !${Ini[Mob_Ignore_List.ini,${Ignores.${Zone.ShortName}},Ignore].Find[${NearestSpawn[${i},npc noalert 1 radius ${PullRange} zradius ${ZRange}].Name}]}) {
                    /if (${Navigation.PathExists[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}) {
                        /varset PullList[${j},1] ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}
                        /varset PullList[${j},2] ${Int[${Navigation.PathLength[id ${NearestSpawn[${i},npc noalert 1 targetable radius ${PullRange} zradius ${ZRadius} "${FarmMob}"].ID}]}]}
                        /if (${j}==1) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        } else /if (${PullList[${j},2]} < ${Shortest}) {
                            /varset PullTargetID ${PullList[${j},1]}
                            /varset Shortest ${PullList[${j},2]}
                        }
                        /varcalc j ${j}+1
                    }
                }
            /next i
            /varset myTargetID ${PullTargetID}
            /setchattitle Going to kill ${Spawn[id ${myTargetID}].CleanName}!
        }
    }
/return


Sub Bind_AddToIgnore
    /alert add 1 ${Target.CleanName}
    /varset myTargetID 0
    /squelch /target clear
/return 


Sub Bind_AddThisIgnore
    /alert add 1 ${Target.Name}
    /varset myTargetID 0
    /squelch /target clear
/return

I get a CTD every single time I try to use this.
 
Not even sure how to begin troubleshooting your issue as CTD is only being reported by you and every time you use it makes it pretty hard to troubleshoot. No idea how to address the issue currently.
 
I was, but returned to default and still had CTD

- - - Updated - - -

Rich (BB code):
#bind AddToIgnore
#bind AddThisIgnore


Sub Main(int Param0, string Param1)
    /alias /ignorethis /call Bind_AddThisIgnore
    /alias /ignorethese /call Bind_AddToIgnore

Removed this section (apart from sub main of course) and the macro works perfectly now. Any clues?
 
shouldn't it be something like this?

Rich (BB code):
#bind AddToIgnore /ignorethis
#bind AddThisIgnore /ignorethese

Sub Main(int Param0, string Param1)

I can't be sure, I haven't used binds that often.
 
Simple Farm Mob Macro

Users who are viewing this thread

Back
Top