So recently I wrote a guide on conditions and understanding /if (Condition) /command statements.
https://www.redguides.com/community/threads/65300-Conditions-and-you-Coding-tutorial-information
A lot of users are interested in learning how to write macros. I understand why too. The only thing better than having MQ2 at your fingertips is the ability to manipulate the information available to MQ2 into something that is custom coded specifically for you. Perhaps you want to share your code with others, and perhaps you don't. The point here is how to create a macro that you can execute in the game, no matter how large or small. I want to explain the requirements of a macro, and hopefully some less obvious methods of writing one that will increase the potential of the macro you're writing.
Main
Every macro requires the main Sub, also known as a Subroutine. If you have a file, let us say Test.mac, and it doesn't have a Sub Main section then your macro will fail to run no matter what you do. So as a requirement for every macro, you must have at least the following:
You can write an entire macro without ever leaving the main Subroutine of the macro, as that is the only one that is required. You'll notice above that I have identified Main as a Sub by putting "Sub Main". I also have "/return" at the end of the Sub to identify the exit point of this Subroutine. When you reach the /return of Sub Main, your macro will end and the MQ2 console window where you typically see information output in text format will say as much.
Echo
An Echo is something that will repeat whatever you put into it to the MQ2 window in game. It is a lot like /say except it's only shown locally on that single instance of Everquest and is not shared with anyone else or other instances of Everquest that you might have running. To "echo" information to the MQ2 window, you need only type "/echo Text goes here" and it will output the text "[MQ2] Text goes here".
Right, so now you know what an echo is. But what is the point of it if only I can see it? Well, in order to physically see what your code is doing, you'll need a way to look at the information as it is processed. What is special about an echo is that it "parses" data from MQ2 in the same way that it would parse it if used in a conditional statement. IE:
/if (${Me.PctHPs} < 50) /casting "Fervid Renewal" -targetid|${Me.ID}
/echo ${Me.PctHPs} < 50
So above in the if statement we are checking to see if our Hit points are below 50%. If they are, then we want to cast Fervid Renewal on ourselves. (Syntax for the above slash command can be found at https://www.redguides.com/community/threads/24833-Redguides-MQ2-Compile-Plugin-List ). Immediately following I show a /echo statement where I "Say" to the MQ2 window my hp percentage, the comparator, and the value I want to compare it to. So based on my previous explanation of conditionals we know that if my percentage of health is 49% or lower then it should execute the slash command that followed.
When something doesn't work as you expect it to work, you can add what we call a "Debug" statement to find out what MQ2 sees when it parses the information of the condition. These will be invaluable when learning how to create a macro for MQ2.
Variables - How to make them, how to change them
So what is a variable? Most people are required to attend algebra at some point during their school years nowadays. Not to say that everyone has, but a large majority of people have dealt with a math problem involving some unknown thing that you have to solve for. Such as 5 - x = 3 where you must sort out what x is using the available information. Well in programming you have x's, except that you can tell the program what x is before it ever uses it. So I want to explain how to create a variable, some things you can't do, and some ways you might want to use a variable.
To create a variable you must "declare" the variable. To declare a variable you must know:
What you want to name the variable.
What type of variable you need (Int/Float/String etc)
What is the scope of the variable (Outer can be accessed by all Sub Routines, Local can only be used by the Subroutine it's currently in, more on that later)
What is the value, if any, of the variable?
the following is the format for creating a variable.
/declare variable type scope value
So if I want to create a variable called "x" that will be a whole number and I need to access it through-out my program with an initial value of -1
/declare x int outer -1
would be what I needed to type. Let's look at this in a macro format now.
What are some things I can't do when creating a variable?
You cannot create a variable that has already been created. (Unless your scope is "local" and the variables are created in different subroutines.)
You cannot create a variable using a "reserved" named. Such as Me. ${Me} is a Top Level Object and thus cannot be created as a variable. However, as mentioned in my previous tutorial, TLO's are case sensitive, so you -can- create a variable "me" ${me} is not the same as ${Me} because they don't share case attributes. However, I recommend against using the same word as other variables with different cases because it makes it complicated to find your variables later on when debugging your macro, it also promotes error where you do/don't hit the shift key and it calls the wrong variable but doesn't create an error because both were valid.
Okay, so now I have my main sub, and I have created a variable with a value of -1. But when you run it, it doesn't do anything. Well, that's because we haven't told it to do anything, only to create the variable and then the macro is done. So we need to ask the program to do something with that variable. Let's use it to do some math.
Enter the Math TLO. For details on how Math works look at the WIKI page for it.
https://www.redguides.com/wiki/TLO:Math
So let us say we want to do 5 - x and then assign the result to ANOTHER variable, and then output that variable to the MQ2 window using an Echo. Well based on that it sounds like we need another variable. I'm going to call it "Solution" because a variable can be named anything that isn't already being used as a variable and isn't a TLO.
Okay, so I've created the variable, and it's time to put it to use. But first, did you notice I didn't give it a value? That was intentional. I'm going to give it a value later, but I need to create the variable so that it can be assigned the value. I could have done this at the same time as I conducted the math problem to save lines of space. In fact, I'll show you both methods. The following is my math problem.
Real quick let's go over what just happened. I added a new slash command. The "varset" command. In order to change a variable once it has been created, you'll need a command to tell the macro to change it. Then you need to tell it which variable is changing, and finally, what to change that variable too. So /varset is my command. The variable I want to change is Solution, and what I want to change it to is the result of ${Math.Calc[5 - ${x}]}
So wait, why is Solution and x surrounded by ${}, they are not TLOs! Well, anytime you call a variable for MQ2 to parse the information from it, you must surround it by ${} in order to identify it as a variable to be parsed.
So what do you think the result of this macro will be?
Well 5 - - 1 == 6 because two negatives make a positive. Sounds like 6 is the result of the macro. If you said 6, then you are a math genius (according to random facebook posts created to make people feel better about themselves).
Great, so now you have a glorified calculator, so how does that help you? Well, it doesn't really help you per say. But I'm trying to start with something small to teach you have to manipulate information with. I mentioned there was another way to handle this, so let's get to work on that.
Okay. So above you'll find method #2 for the same macro with the same result as the previous set of code, except that we gave "Solution" a value while we were declaring it using the same information we used in the previous /varset
There is one more way to manipulate a variable that I'm aware of. That is the "/varcalc" command. The varcalc is the same as varset, but it's like saying you want to use the ${Math[]} TLO on it. So if we want to use varcalc it's a lot like varset.
/varcalc Solution 5-${x}
is the same as
/varset Solution ${Math.Calc[5-${x}]}
I issue the command, I tell MQ2 which variable and then I tell it what to change the variable to. Except with varcalc I can do math with less typing than if I used a varset command.
So as you can see, even with 5-7 lines of code I have 3 different ways that I could create this macro by simply changing when I assign a value to a variable, or how I calculate the value to assign the variable. So there is not "right" way to do it, just a matter of which one suits your own personal needs. Your needs being: accomplishing the desired result, and understanding what you are looking at.
Additional Subroutines and why you should use them
Well, the idea of coding a script is to automate some task for you that you do often. Such as going through your bags and finding all your trade skill items and depositing them into the bank. Well, if I told you that you could automate the automation to an extent, would you not prefer to do things that way? I know that I certainly would prefer to do things that way. Let me explain the creation of another subroutine and how to use it.
For starters, let us invent a problem we need to solve that justifies creating another subroutine. A simple one-off math problem isn't really a good enough reason to do it. But what if you had to do several hundred math problems. That's a lot of variables and calculation to type over and over again. This is certainly justification for a new subroutine. So the creation of an additional subroutine is the same as the creation of Sub Main. Also, much like a variable, you can call the subroutine anything you want so long as it isn't currently being used by another subroutine. Let's create a math subroutine for addition.
So in the above example, the only thing the routine does is add 5 and x together, and assigns that value to Solution. Remember, Solution and x are both "outer" variables in scope and can be used by the entire macro regardless of the subroutine it was created in.
So this doesn't really make any sense to do it this way. Because I can still only do the one calculation. So how do I make it so that I can add two numbers together that aren't set in stone? Well, for that you'll need what is called a parameter, which you will use when "calling" the AddTwo subroutine. A parameter is a variable that is automatically defined locally to the Subroutine when it is entered and disappears when you leave the subroutine. To create a parameter, you need only add a (parameter) to the end of the Sub AddTwo line. where "parameter" is the name of the variable you want to create. Then in order to call the subroutine, you issue a command "/call subroutineName parameter" where /call is the command, subroutineName is the name of the subroutine (AddTwo in this case) and -1 is the value being passed to the 1st parameter of the AddTwo subroutine. In our example below, we're going to create TWO parameters, so that we can select the two numbers to add together each time we call the subroutine.
So above is the modification to our macro. Every variable that we use is coded into the new subroutine as a local variable and only exists inside of that subroutine, but I can call it from Sub Main and add any two numbers I want together and output the result to the MQ2 window for me to see. But....now instead of 5 to 7 lines, it's 8 lines of code. Right, but without the added subroutine, any subsequent calls using the previous method would cause the number of lines to double. Using this method I only add 1 line per additional problem I do. Such as the below calls.
/call AddTwo 4 2
/call AddTwo 9 5
/call AddTwo ${Me.Level} ${Group.Member[1].Level}
NOTE: calls to subroutines are NOT case sensitive. /call AddTwo and /call aDDtWO are still the same call.
So you noticed that I didn't assign a type for the parameters a and b? Well, the macro interprets what kind of information is being used and automatically assigns it that type. But, if you're OCD, or don't want to accidentally pass something that is supposed to be an integer, a value that is a float, or a string, then you can assign it the type as well.
Sub AddTwo(int a, int b)
by simply adding the type to the front of the parameter's variable name.
Okay, so you already know how to add two numbers in your head. So how is any of this really useful? Well I'll take an example out of my tutorial.mac for use here in this tutorial.
So above I have a subroutine from tutorial.mac, some of the information you may understand and some of it you may not. I'm going to go over the purpose and usage of this sub and not into details about things I haven't explained yet.
MoveToWait is the name of this sub, and I selected that name because it is using the /moveto command of MQ2MoveUtils to move your character from your current location to another location in a straight line distance, and it waits until you are done with that movement before it lets the macro execute any more code. It also calls two other subroutines repeatedly while it waits for the move to be completed as a way to keep track of where your character is, and if your merc has been killed at any time during the movement. It's not a very big subroutine, but it does a lot. I could type the above subroutine and the sub routines it calls every time I want the character to move, but considering tutorial.mac is about 1800 lines of code as it sits, I figure I've done enough typing already, if macros didn't use subroutines they would be exponentially larger files, and work loads to create. Macros like KissAssist.mac exceed 10k lines of code and has over 200 subroutines that it calls. So if it wasn't using subroutines, not only would it be less efficient and a clusterfuck to add things to, it would also be somewhere in the neighborhood of 100k to 250k lines of code.
Flow of Code
So you see how to make and call subroutines, but what is going on? I don't understand.
Well when you call a subroutine it leaves the current routine and goes to the other routine and executes all of its code before returning. When it returns back to the current routine it's directly below the line that was just called. I've attached a GIF that should show you what I'm talking about (Though it's pretty low quality).
https://www.redguides.com/community/threads/65300-Conditions-and-you-Coding-tutorial-information
A lot of users are interested in learning how to write macros. I understand why too. The only thing better than having MQ2 at your fingertips is the ability to manipulate the information available to MQ2 into something that is custom coded specifically for you. Perhaps you want to share your code with others, and perhaps you don't. The point here is how to create a macro that you can execute in the game, no matter how large or small. I want to explain the requirements of a macro, and hopefully some less obvious methods of writing one that will increase the potential of the macro you're writing.
Main
Every macro requires the main Sub, also known as a Subroutine. If you have a file, let us say Test.mac, and it doesn't have a Sub Main section then your macro will fail to run no matter what you do. So as a requirement for every macro, you must have at least the following:
Rich (BB code):
Sub Main
/return
You can write an entire macro without ever leaving the main Subroutine of the macro, as that is the only one that is required. You'll notice above that I have identified Main as a Sub by putting "Sub Main". I also have "/return" at the end of the Sub to identify the exit point of this Subroutine. When you reach the /return of Sub Main, your macro will end and the MQ2 console window where you typically see information output in text format will say as much.
Echo
An Echo is something that will repeat whatever you put into it to the MQ2 window in game. It is a lot like /say except it's only shown locally on that single instance of Everquest and is not shared with anyone else or other instances of Everquest that you might have running. To "echo" information to the MQ2 window, you need only type "/echo Text goes here" and it will output the text "[MQ2] Text goes here".
Right, so now you know what an echo is. But what is the point of it if only I can see it? Well, in order to physically see what your code is doing, you'll need a way to look at the information as it is processed. What is special about an echo is that it "parses" data from MQ2 in the same way that it would parse it if used in a conditional statement. IE:
/if (${Me.PctHPs} < 50) /casting "Fervid Renewal" -targetid|${Me.ID}
/echo ${Me.PctHPs} < 50
So above in the if statement we are checking to see if our Hit points are below 50%. If they are, then we want to cast Fervid Renewal on ourselves. (Syntax for the above slash command can be found at https://www.redguides.com/community/threads/24833-Redguides-MQ2-Compile-Plugin-List ). Immediately following I show a /echo statement where I "Say" to the MQ2 window my hp percentage, the comparator, and the value I want to compare it to. So based on my previous explanation of conditionals we know that if my percentage of health is 49% or lower then it should execute the slash command that followed.
When something doesn't work as you expect it to work, you can add what we call a "Debug" statement to find out what MQ2 sees when it parses the information of the condition. These will be invaluable when learning how to create a macro for MQ2.
Variables - How to make them, how to change them
So what is a variable? Most people are required to attend algebra at some point during their school years nowadays. Not to say that everyone has, but a large majority of people have dealt with a math problem involving some unknown thing that you have to solve for. Such as 5 - x = 3 where you must sort out what x is using the available information. Well in programming you have x's, except that you can tell the program what x is before it ever uses it. So I want to explain how to create a variable, some things you can't do, and some ways you might want to use a variable.
To create a variable you must "declare" the variable. To declare a variable you must know:
What you want to name the variable.
What type of variable you need (Int/Float/String etc)
What is the scope of the variable (Outer can be accessed by all Sub Routines, Local can only be used by the Subroutine it's currently in, more on that later)
What is the value, if any, of the variable?
the following is the format for creating a variable.
/declare variable type scope value
So if I want to create a variable called "x" that will be a whole number and I need to access it through-out my program with an initial value of -1
/declare x int outer -1
would be what I needed to type. Let's look at this in a macro format now.
Rich (BB code):
Sub Main
/declare x int outer -1
/return
What are some things I can't do when creating a variable?
You cannot create a variable that has already been created. (Unless your scope is "local" and the variables are created in different subroutines.)
You cannot create a variable using a "reserved" named. Such as Me. ${Me} is a Top Level Object and thus cannot be created as a variable. However, as mentioned in my previous tutorial, TLO's are case sensitive, so you -can- create a variable "me" ${me} is not the same as ${Me} because they don't share case attributes. However, I recommend against using the same word as other variables with different cases because it makes it complicated to find your variables later on when debugging your macro, it also promotes error where you do/don't hit the shift key and it calls the wrong variable but doesn't create an error because both were valid.
Okay, so now I have my main sub, and I have created a variable with a value of -1. But when you run it, it doesn't do anything. Well, that's because we haven't told it to do anything, only to create the variable and then the macro is done. So we need to ask the program to do something with that variable. Let's use it to do some math.
Enter the Math TLO. For details on how Math works look at the WIKI page for it.
https://www.redguides.com/wiki/TLO:Math
So let us say we want to do 5 - x and then assign the result to ANOTHER variable, and then output that variable to the MQ2 window using an Echo. Well based on that it sounds like we need another variable. I'm going to call it "Solution" because a variable can be named anything that isn't already being used as a variable and isn't a TLO.
Rich (BB code):
Sub Main
/declare x int outer -1
/declare Solution int outer
/return
Okay, so I've created the variable, and it's time to put it to use. But first, did you notice I didn't give it a value? That was intentional. I'm going to give it a value later, but I need to create the variable so that it can be assigned the value. I could have done this at the same time as I conducted the math problem to save lines of space. In fact, I'll show you both methods. The following is my math problem.
Rich (BB code):
Sub Main
/declare x int outer -1
/declare Solution int outer
/varset Solution ${Math.Calc[5 - ${x}]}
/echo 5 - ${x} = ${Solution}
/return
Real quick let's go over what just happened. I added a new slash command. The "varset" command. In order to change a variable once it has been created, you'll need a command to tell the macro to change it. Then you need to tell it which variable is changing, and finally, what to change that variable too. So /varset is my command. The variable I want to change is Solution, and what I want to change it to is the result of ${Math.Calc[5 - ${x}]}
So wait, why is Solution and x surrounded by ${}, they are not TLOs! Well, anytime you call a variable for MQ2 to parse the information from it, you must surround it by ${} in order to identify it as a variable to be parsed.
So what do you think the result of this macro will be?
Well 5 - - 1 == 6 because two negatives make a positive. Sounds like 6 is the result of the macro. If you said 6, then you are a math genius (according to random facebook posts created to make people feel better about themselves).
Great, so now you have a glorified calculator, so how does that help you? Well, it doesn't really help you per say. But I'm trying to start with something small to teach you have to manipulate information with. I mentioned there was another way to handle this, so let's get to work on that.
Rich (BB code):
Sub Main
/declare x int outer -1
/declare Solution int outer ${Math.Calc[5 - ${x}]}
/echo 5 - ${x} = ${Solution}
/return
Okay. So above you'll find method #2 for the same macro with the same result as the previous set of code, except that we gave "Solution" a value while we were declaring it using the same information we used in the previous /varset
There is one more way to manipulate a variable that I'm aware of. That is the "/varcalc" command. The varcalc is the same as varset, but it's like saying you want to use the ${Math[]} TLO on it. So if we want to use varcalc it's a lot like varset.
/varcalc Solution 5-${x}
is the same as
/varset Solution ${Math.Calc[5-${x}]}
I issue the command, I tell MQ2 which variable and then I tell it what to change the variable to. Except with varcalc I can do math with less typing than if I used a varset command.
Rich (BB code):
Sub Main
/declare x int outer -1
/declare Solution int outer
/varcalc Solution 5 - ${x}
/echo 5 - ${x} = ${Solution}
/return
So as you can see, even with 5-7 lines of code I have 3 different ways that I could create this macro by simply changing when I assign a value to a variable, or how I calculate the value to assign the variable. So there is not "right" way to do it, just a matter of which one suits your own personal needs. Your needs being: accomplishing the desired result, and understanding what you are looking at.
Additional Subroutines and why you should use them
Well, the idea of coding a script is to automate some task for you that you do often. Such as going through your bags and finding all your trade skill items and depositing them into the bank. Well, if I told you that you could automate the automation to an extent, would you not prefer to do things that way? I know that I certainly would prefer to do things that way. Let me explain the creation of another subroutine and how to use it.
For starters, let us invent a problem we need to solve that justifies creating another subroutine. A simple one-off math problem isn't really a good enough reason to do it. But what if you had to do several hundred math problems. That's a lot of variables and calculation to type over and over again. This is certainly justification for a new subroutine. So the creation of an additional subroutine is the same as the creation of Sub Main. Also, much like a variable, you can call the subroutine anything you want so long as it isn't currently being used by another subroutine. Let's create a math subroutine for addition.
Rich (BB code):
Sub AddTwo
/varcalc Solution 5 + ${x}
/return
So in the above example, the only thing the routine does is add 5 and x together, and assigns that value to Solution. Remember, Solution and x are both "outer" variables in scope and can be used by the entire macro regardless of the subroutine it was created in.
So this doesn't really make any sense to do it this way. Because I can still only do the one calculation. So how do I make it so that I can add two numbers together that aren't set in stone? Well, for that you'll need what is called a parameter, which you will use when "calling" the AddTwo subroutine. A parameter is a variable that is automatically defined locally to the Subroutine when it is entered and disappears when you leave the subroutine. To create a parameter, you need only add a (parameter) to the end of the Sub AddTwo line. where "parameter" is the name of the variable you want to create. Then in order to call the subroutine, you issue a command "/call subroutineName parameter" where /call is the command, subroutineName is the name of the subroutine (AddTwo in this case) and -1 is the value being passed to the 1st parameter of the AddTwo subroutine. In our example below, we're going to create TWO parameters, so that we can select the two numbers to add together each time we call the subroutine.
Rich (BB code):
Sub Main
/call AddTwo 5 -1
/return
Sub AddTwo(a, b)
/declare Solution int local
/varcalc Solution ${a} + ${b}
/echo ${a} + ${b} = ${Solution}
/return
So above is the modification to our macro. Every variable that we use is coded into the new subroutine as a local variable and only exists inside of that subroutine, but I can call it from Sub Main and add any two numbers I want together and output the result to the MQ2 window for me to see. But....now instead of 5 to 7 lines, it's 8 lines of code. Right, but without the added subroutine, any subsequent calls using the previous method would cause the number of lines to double. Using this method I only add 1 line per additional problem I do. Such as the below calls.
/call AddTwo 4 2
/call AddTwo 9 5
/call AddTwo ${Me.Level} ${Group.Member[1].Level}
NOTE: calls to subroutines are NOT case sensitive. /call AddTwo and /call aDDtWO are still the same call.
So you noticed that I didn't assign a type for the parameters a and b? Well, the macro interprets what kind of information is being used and automatically assigns it that type. But, if you're OCD, or don't want to accidentally pass something that is supposed to be an integer, a value that is a float, or a string, then you can assign it the type as well.
Sub AddTwo(int a, int b)
by simply adding the type to the front of the parameter's variable name.
Okay, so you already know how to add two numbers in your head. So how is any of this really useful? Well I'll take an example out of my tutorial.mac for use here in this tutorial.
Rich (BB code):
Sub MoveToWait(int Y, int X, int Z)
/moveto loc ${Y} ${X} ${Z}
/while (${MoveTo.Moving}) {
/call WhereAmI
/call CheckMerc
/delay 10
}
/return
So above I have a subroutine from tutorial.mac, some of the information you may understand and some of it you may not. I'm going to go over the purpose and usage of this sub and not into details about things I haven't explained yet.
MoveToWait is the name of this sub, and I selected that name because it is using the /moveto command of MQ2MoveUtils to move your character from your current location to another location in a straight line distance, and it waits until you are done with that movement before it lets the macro execute any more code. It also calls two other subroutines repeatedly while it waits for the move to be completed as a way to keep track of where your character is, and if your merc has been killed at any time during the movement. It's not a very big subroutine, but it does a lot. I could type the above subroutine and the sub routines it calls every time I want the character to move, but considering tutorial.mac is about 1800 lines of code as it sits, I figure I've done enough typing already, if macros didn't use subroutines they would be exponentially larger files, and work loads to create. Macros like KissAssist.mac exceed 10k lines of code and has over 200 subroutines that it calls. So if it wasn't using subroutines, not only would it be less efficient and a clusterfuck to add things to, it would also be somewhere in the neighborhood of 100k to 250k lines of code.
Flow of Code
So you see how to make and call subroutines, but what is going on? I don't understand.
Well when you call a subroutine it leaves the current routine and goes to the other routine and executes all of its code before returning. When it returns back to the current routine it's directly below the line that was just called. I've attached a GIF that should show you what I'm talking about (Though it's pretty low quality).