Well writing a plugin and writing a macro are of course two different animals. I've debated on the details of how I might approach it, and honestly without more understanding of the macro as a whole it's really hard to say. But I believe any movement involving engaging a target should be handled with a function and not a /moveto and not a /stick and not a /nav. It should be determined within the function the best approach to use.
Let us say that we have determined that it's time to engage the target, and we need to know the best way to do so. I'd need to decide how to approach that movement by first ensuring that I wasn't using any other method of movement. Since KA uses /moveto, /stick, and /nav I would start by ensuring that none of those are currently active, and if they are then I'd need to switch them off so that I could issue my new command.
C++:
void ShouldIStick() {
if (!bUseStick) return;
if (Casting()) return;
if (iPluginMode != MODE_MANUAL) {
if (HandleMoveUtils() && (*pbStickOn == false || (TargetOfTarget() && Me() && TargetOfTarget()->SpawnID == Me()->SpawnID && StickMode != Have_Aggro) || (TargetOfTarget() && Me() && TargetOfTarget()->SpawnID != Me()->SpawnID && StickMode != No_Aggro && iPluginMode != MODE_PULLERTANK && iPluginMode != MODE_TANK)) && (!NavActive() || LineOfSight(Me(), MyTarget()))) {
if (NavActive())
NavEnd();
if (TargetOfTarget() && Me() && TargetOfTarget()->SpawnID == Me()->SpawnID || (iPluginMode == MODE_TANK || iPluginMode == MODE_PULLERTANK)) {
if (StickMode != Have_Aggro) {
Stick(StickHaveAggro);
StickMode = Have_Aggro;
}
}
else if (TargetOfTarget() && Me() && TargetOfTarget()->SpawnID != Me()->SpawnID && StickMode != No_Aggro || *pbStickOn == false) {
Stick(StickHow);
StickMode = No_Aggro;
}
}
}
}
C++:
void ShouldINav() {
if (iPluginMode != MODE_MANUAL) {
if (!ValidTarget(MyTargetID())) {
if (IsAttackOn()) {
AttackOff();
}
ClearTarget();
return;
}
if (PSPAWNINFO pSpawn = (PSPAWNINFO)GetSpawnByID(MyTargetID())) {
if ((!bUseStick || !LineOfSight(Me(), pSpawn)) && Get3DDistance(pSpawn->X, pSpawn->Y, pSpawn->Z, Me()->X, Me()->Y, Me()->Z) > 15) {
if (!NavActive()) {
NavigateToID(pSpawn->SpawnID);
}
}
if (AmFacing(pSpawn->SpawnID) > 10) Face(GetCharInfo()->pSpawn, "fast");
}
}
}
Those are my two movement commands that are done for the combat routine for all classes currently. Being mindful that the plugin loops repeatedly approximately every 0.4 to 0.6 seconds though all the logic. But, if attack is on the following is done by all classes (Though this is subject to change as I'm currently in the process of adding casters)
C++:
bool CommonCombatDPS() {
PSPAWNINFO me = (PSPAWNINFO)GetCharInfo()->pSpawn;
if (!me || !MyTarget() || !MyTargetID() || MyTargetID() == me->SpawnID) {
if (IsAttackOn())
AttackOff();
return false;
}
SwitchWithMACheck();
if (!Casting()) {
ShouldINav();
ShouldIStick();
}
if (PullTarget) PullTarget = 0;
return true;
}
So I'm saying in this CommonCombatDPS() function that if I exist in game, lets look at some reasons to stop attacking, or not do any movement.
If I don't have a target, or I'm targeting myself, I want to turn off attack and leave the Common DPS checks. (Targeting yourself may be valid during combat for non-melee classes who do actually cast buffs, and this situation will be revisited for my plugins soon as I make the cleric)
Then I do a check for the function that auto switches with the MA, which just say if the MA's target is different than my target to switch to their current target, but only if SwitchWithMA is true.
Then, if I'm not currently casting anything, check those two functions. ShouldINav and ShouldIStick.
If I'm in combat, and I had a designated pull target, I want to clear it, because I'll need to re-evaluate my pull target after combat because it's possible something else has spawned, or the spawn I had wanted to pull has now moved.
then I return. The return being true or false is an indication on if I should continue to process the rest of the normal combat routine. So if I didn't have a target, or the target was myself in this case I would return false, and I wouldn't finish the rest of the combat routine.
Note that nothing in this routine has me locked to a specific target with the exception of SwitchWithMA() check which is designed to make me target anything the MA targets. (WIP)
For the movement stuff ShouldINav and ShouldIStick, in both cases we check for a valid target. KA does this in their code, so I shouldn't have to explain this part. This just ensures that the target I have right now is a valid combat target, and if it isn't it will turn off attack and clear my target.
For the actual part where navigation/movement happens I honestly only need to check a few things.
bUseStick is a bool value that stores if the user wants to use MQ2MoveUtils to stick to their target, which allows them to use the StickHow method to stick behind a target, or to the left and right sides. If that's not on, then we're going to always use nav.
!LineOfSight is making saying if I don't have line of sight, then I want to use nav, because I can't see my target to hit them. I do NOT want to pass this function and use stick in that case because if I can't see my target then sticks straight line distance engage isn't going to work, and finally If the distance from me to the mob is greater than 15 (exceeds melee distance in most cases at about 18 distance). With that said, this of course assumes some things and would need to be modified for caster classes to allow the casters to not stand right on top of the mobs they are engaging.
So if I pass that and nav is not currently active. I need to navigate to the target. then, because in some cases nav doesn't actually move, I may need to make the user face their target, I issue a face command so they face the enemy.
For ShouldIStick
I again check for bUseStick, and if it's false we just leave the function because we don't want to use stick to get to the target.
HandleMoveUtils just validates I have access to the pbStickOn pointer, and that the plugin mq2moveutils is loaded.
*pbStickOn is checking is stick is currently active by comparing it to false, there are macro equivilents to this in KA that can be used.
Stick mode includes new information that wasn't located in the ShouldINav function, and it's related to "StickMode" and "StickHow"
StickMode determines what mode of sticking I'm currently using. If I'm "/stick behind 10 uw" then StickMode is No_Aggro which is 2. and StickMode Have_Aggro is 1. This keeps track of how I am sticking using an int as opposed to me asking MQ2MoveUtils to tell me what it's doing.
So which "StickMode" to use in this case is determined based on if I'm the TargetOfTarget of the NPC I'm currently targeting. So if the mob wants to eat me, then I just /stick, if the mob is interested in someone else, I want to /stick behind.
So as long as I don't have aggro I'm using the StickHow. provided by the user. Otherwise it's preset to not spin in circles around the mob trying to stay behind or to the left/right of the mob.
For the movement to camp, from camp, or to chase the MA. I 100% recommend using another function, or duplicating the method you travel in those various places. IE: a function for MoveToLoc, which takes the XYZ destination and determines the best method similar to the approach I've taken for the above combat routine.
In dungeons, you may not have a direct path back to camp, and /moveto will run into walls and all sorts of silly things trying to get back because it has no concept of walls, or other barriers. When a mesh is loaded and a path exists, I would use Nav 100% of the time. If there is no mesh loaded, then I could see just making it work with a /moveto command because you no longer have an option of using recast navigation to travel back to the camp.
The same for following in chase mode. Navigation should be used anytime it's possible to use navigation because it's the most accurate method of getting to a destination.
I realize that traveling using nav was not more widely adopted until more recently so I'm not knocking the methods that were used because they were certainly valid in their time, but with the addition of the mesh updater and repositories, we'd certainly want to use that method of travel where ever and when ever it's possible.
My plugins don't use /moveto or /stick at all, but KA is already setup to use those methods. So adding logic to try to use nav to the top of any existing attempts to /moveto should prove more time consuming than it would be complicated.
100% of my movement issues with KA revolve around the multiple methods of movement, and two of them fighting each other for control of the character. The character tries to return to camp, but something else is trying to get them to engage in combat. So instead of finishing either of them, they are just locked in place where they are standing spinning in circles with neither method of movement winning the battle. This has been most obvious on my bard, where it runs out to engage in combat, finishes a fight, tries to return to camp, but another mob comes in, now mid movement they're told to stick to the mob but a /moveto is still active somewhere.
This is the part where I'm saying we should be checking and ending any other movement. Theres ${MoveTo} and ${Stick} datatypes available that allow you to make sure one or the other isn't running when you attempt to use the command. For the case of kissassist stick is trying to attach to something specific. Like a player or a mob. I believe the priority should be given to Stick over MoveTo as moveto is trying to arrive at a destination, but if we need to follow a mob or player then we should end any /moveto commands then process with issuing a stick command. The wiki for MQ2MoveUtils was greatly expanded a while back to include all the details of these TLO's so that we can approach them with as much information possible available to us.
Keep in mind that this all sounds good in practice, implementation might not be as straight forward as it sounds in this writeup.
There's over 70 /stick commands issues through-out KissAssist.mac, about half that many /moveto commands issued, and a fair amount of nav commands issued. Going through and find each one of these and recoding it to use some predefined method such as the one documented here is surely going to be a monster of a task. I had the luxury of writing all of my movement while I was writing all the logic with this thought process in mind. Recoding it to do things differently, especially not being the original author of any of the logic will surely be mind numbing, and a process that should be expected to take a while.

The above screenshots are a result of "Find all in document" for both /stick and /moveto to show the various differences from one moveto/stick command use to the next.
However, once complete would allow you to hopefully reduce the over all amount of code that is stored in memory. Allow the sticking to be consistence, and improve the overall functionality of the macro.