• 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

Guide - Macros - Where to start? (1 Viewer)

ChatWithThisName

⚔️🛡️🗡️
Moderator
Joined
Dec 29, 2017
RedCents
13,975¢
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:

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/forums/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).
 

Attachments

  • Code_Flow.gif
    Code_Flow.gif
    142.2 KB · Views: 278
Great Intro to Macros!

This might be a bit intermediate, but I was always wondering why /return from Sub was not returning values.

Reading the MQ2 manual it turns out you can in fact return values.
https://www.macroquest2.com/includes/wassup/manual.php#macsubs

You use ${Macro.Return} which keeps whatever the last /call returned.

It is a bit annoying as you can't directly assign return values as in a normal programming language. :)

Rich (BB code):
Sub Main
	/declare MyGlob int global
	/call AddTwo 5 9
	/echo ${Macro.Return}
	/varset MyGlob ${Macro.Return}
	/echo ${MyGlob}
/return

Sub AddTwo(a, b)
    /declare Solution int local
    /varcalc Solution ${a} + ${b}
    /echo ${a} + ${b} = ${Solution}
/return ${Solution}

You'll see that in the few places that kissassist.mac uses returning values, it places a sanity check:
Rich (BB code):
 /if (${Macro.Return.Length}) /varset DPSMyDam ${Macro.Return}

That is if the last Sub returned something, then use it.
 
Yeah, I considered going in to returning values from a subroutine but figured I would save that for when I got into more depth. After going over conditions, I figured the next step was routines, and perhaps returning values and accessing them might be too much. But now that I think about it, I should probably add it to this tutorial because it's part of the fundamentals of routines.
 
Great guide! I think it'd be more useful to intermediate coders than people looking to get started though.


The second half of the guide seems to be focused mostly on variables and math which will go right over almost everyone's head who doesn't have a lot of knowledge with coding already. It's making things initially too complex for most noobs to understand, even with my knowledge in HTML and limited MQ2 I still don't understand what it is, where you're going with it or what it all means.


So, fantastic guide - but I wouldn't neccessarily call it a "where to start" or "for beginners" guide. I think that most of the info taught in the guide a noob/someone with no coding experience would be confused by.
 
@LurkMcGurk the second line of the post has a link to what you should have read first. There is no easy way to introduce some of the information about Top level objects. In cases where I used the Math TLO, I also offered an alternative that is much easier to understand and use. But showed multiple methods of achieving the same result. If there is something specific that you don't understand then say specifically what you don't understand about it. Without specifics, I can't address any short-comings in the information to make it clearer. But to be sure, if variable manipulation and addition/subtraction math are causing you issues, then perhaps you shouldn't quit your day job.

This is intermediate to an extent. Conditions were the beginner things. I haven't even got to if/else statements, nested if statements, for loops, while loops, arrays, window manipulation etc. There is a lot of information to cover to create a macro. Understanding how to create a variable, how to manipulate a variable, and how subroutines work seem like a logical step in a forward direction.
 
@LurkMcGurk the second line of the post has a link to what you should have read first.

Then why the bloody 'ell is this called "where to start"? =P

There is no easy way to introduce some of the information about Top level objects. In cases where I used the Math TLO, I also offered an alternative that is much easier to understand and use. But showed multiple methods of achieving the same result. If there is something specific that you don't understand then say specifically what you don't understand about it. Without specifics, I can't address any short-comings in the information to make it clearer.

If this isn't a "beginner" guide then I have nothing bad to say about it. My sole complaint was that I barely understand the guide which means there's no way a beginner would. I've made a few macros myself and have never had to learn variables so I didn't know why that was a good place to start in teaching people how to use MQ2 or write macros. I still don't really think that it is.

In your guide you start using lingo that new people won't know anything able like, solution, int, float, string, scope, varcalc, loops, arrays, window manipulation... none of these terms were explained in this guide nor is there a compendium you link to that explains what these terms mean (it doesn't explain them in your previous post either). Since this thread has a title "Macros - Where to start?" you make it seem like it's friendly to new users/people just starting out which is why I brought it up.
 
I got a feeling this is going to blow your mind, but it's blatantly obvious that you haven't clicked on and read the content from the link at the very beginning of my post. Because if you had clicked on the link and read the thread you would know what a float, integer, and string are.

"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)" - There you are, scope. Seems you're not fond of reading.

"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. " - Again, failing to read.

Solution.....I declare that as a variable, it means fuck all. I just decided to name a variable Solution. I could have named it "AnswerToTheSuperComplicatedMathProblem" if I wanted. But I don't want to type all that.

No-where in my original post do I mention Arrays, loops, or Window Manipulation.

I'm getting the feeling that you're just here to trash talk or troll the post. Both of which I'm no longer interested in dealing with. If you want to learn, read and digest the materials before complaining about stuff that is present in the guides. I can't save you from yourself.
 
I'm getting the feeling that you're just here to trash talk or troll the post. Both of which I'm no longer interested in dealing with. If you want to learn, read and digest the materials before complaining about stuff that is present in the guides. I can't save you from yourself.

Eh, this about sums it up. I wasn't complaining, I was attempting to help you but you misinterperate it as an insult against your character, which it isn't. I've tried to help people like you but I can't ever figure out how to get through; you're just doomed to make the same mistakes again and again, so - as you suggest, I will just fuck off and stop trying to help you.

If I was here to trash talk or troll your post I would have told you to stick to your day job & don't try writing guides. Instead I tried to offer you constructive criticism, something I have never seen you take well. You always blame the user for being too stupid, as you did again here.
 
This is awesome, you'd have to be either an asshole or really maths deficient to not follow this. (TBH, I am maths deficient as I barely made it to pre-algebra in my school days and I was able to follow.)

I very well could have been an asshole, I am willing to live with my mistake though if it's one that I made. No one is perfect, everyone makes at least a handful of mistakes every single day and if you think otherwise, you're obviously just not thinking hard enough. The only way to learn a majority of the time is by making a mistake and correcting for that error in the future. I don't think that I was an asshole here, but I can point to one time that I was - just ask Razkle when he first made EoKnuke. My intentions were pure when I made my posts on his thread but I quickly realized after the fact that my approach was wrong & I was being a dickhead. I apologized to him two or three times about it too.

Additionally, when talking about anything that's actually worth talking about... One or both parties are going to end up offended or emotional in some way. You can't talk about anything meaningful in life without risking being offended or offending someone, that's just how it works. Now, armed with that knowledge you have two options... 1) You can grow up and deal with having your feelings hurt sometimes to talk about things that matter or 2) You can freak out and act like a little child when someone tells you something you don't want to hear.

This of course changes if you assume that one or both parties are acting in bad faith. Chatwiththisname assumed (incorrectly) I was acting in bad faith because he's accustomed to the EQ community now that he's been here for a few months, where just about everyone acts in bad faith. That's exactly why I avoid being an active part of any online gaming community. It changes everyone for the worse, I have watched multiple people (not including Chatwiththisname) go from being moderately well adjusted individuals to assuming that anything negative said about them was an attack on them.

If you actually spend a day thinking about this too, it'll be obvious to you that it's true (it's basically another version of an echochamber). If you surround yourself with people on a daily basis who act in bad faith you're going to start assuming everyone you interact with on a daily basis is acting in bad faith. It's a reactionary defensive mechanism... A good metaphor to use is when people or dogs are a victim of abuse they typically flinch/cower when someone extends a hand to them - even if the person extending the hand has pure intentions.

And of course, this goes both ways too. I come from an environment where most people act in good faith towards each other on a daily basis. When someone tells me I am wrong about something I immediately assume that they're right NOT that they're trying to hurt me or be an asshole. That's why I end up 'wasting' 30 minutes of my time typing up posts like these and the ones before it trying to help people that don't want my help. When someone at work approaches me and tells me I am wrong about something we typically sit down and talk about it and, together, try to figure out what the facts are. Part of me expects the same thing when I come to Redguides & Everquest despite knowing that the community here is the polar opposite.


P.S - I say 'wasting' in quotes because I am still unsure if making posts like this are a waste of my time. I've met some really nice people on Redguides and in Everquest, people that I am actually proud of calling friends. The reason I have met these types of people though are solely because I make posts like these/am who I am. People who are honest, trustworthy and genuine look for other people who are like themselves to become friends with them. By making posts like these, stating what I believe to be true, accepting criticism and being genuine attracts the exact types of people that I want to be friends with while at the same time pissing off anyone that I wouldn't want to be friends with. That's why I 'waste' time making posts like these.
 
how do i target someone who says something ie someone says. hi ( i then want to be able to target that person that said hi ) . ive been scowering the forums trying to find it. i know it exsits i just dont know it.
 
Would be something like this, from my limited experience learning "how to" -)

Rich (BB code):
#Event Hi "#1# says, 'Hi |${Me.Name}|'"

Sub Main
/declare MyTarget string outer

:MainLoop
/doevents
/goto :MainLoop
/return

Sub Event_Hi(Line,Sender)
/varset MyTarget ${Sender}
/target id ${Spawn[PC ${MyTarget}].ID}
/say Hello ${MyTarget}
/return

May be able to do it easier, but should work, not sure want you need exactly, but this should target another player and respond with "Hello >target name<" in responce to "Hi >mac runners name<"
 
Guide - Macros - Where to start?

Users who are viewing this thread

Back
Top