Tutorial: loops and macro flow control

PeteSampras

Your UI is fucking you. Stop using it.
Joined
Dec 12, 2007
Messages
3,956
Reaction score
49
Points
38
Loops allow your macro to cycle through things more than once or until conditions are met. They keep the macro flowing to the correct location. They allow you to get from point a to point b in or out of a subroutine. You can read about flow control here: http://www.macroquest2.com/wiki/index.php/Flow_Control and about a plugin that allows for additional loop types here: MQ2Loops - MMOBugs Wiki

The basic loop you see in macros is the label and /goto label.

This example will use a label, perform an /echo, and then go back to the label and keep repeating forever.
Code:
Sub Main
:label
/echo hi
/goto :label
/return
Let's say I only wanted to do it 10 times. I could use a counter and an /if () statement to skip the /goto once it reaches 10:

Code:
Sub Main
/declare x byte
:label
/varcalc x ${x}+1
/echo hi
/if (${x}!=10) /goto :label
/return
If my whole goal was to just add up to 10, I could have used a /for loop instead. a /for, /next loop will take a scenario, and perform cycles to check each one. /for,/next format:
Code:
/for variable value1 [to/downto] value2 [increment]
/for a 1 to 10
/for a 2 to 20 step 2
/for a 20 downto 0
/for a 20 downto 10 step 2
Code:
/next a
So for the same adding up to 10 scenario using a /for loop:
Code:
Sub Main
/declare x byte
/for x 1 to 10
/echo hi
/next x
/return
say I want to loop through every spell in my spellbook and do a count of rank 3 spells. I need 2 variables. 1 to use in the /for loop, and 1 to hold the count of rank 3. Then I can cycle all my spells using the /for loop counter.
Code:
Sub Main
/declare x int
/declare i int
/for x ${Me.FirstBook} to ${Me.LastBook}
    /if (${Me.Book[${x}].Find[Rk. III]}) /varcalc i ${i}+1
/next x
/echo ${i}
/return
Or maybe I want to check that if any of my group member's hitpoints are below 50%, to /call a heal routine. /call is used to go directly to another subroutine and serves as an external loop mechanism. And I want to use a label and /goto to permanently check my groups hitpoints over and over until i /end the macro manually. You can also pass variables directly to subroutines via the use of parameters. In this example, I will pass the group member's number to get a heal from one routine to another.
Code:
Sub Main
/declare i int
:label
/for i 0 to ${Group}
   /if (${Group.Member[[COLOR=Lime]${i}[/COLOR]].PctHPs}<=50) /call Heal [COLOR=Lime]${i}[/COLOR]
/next i
/goto :label
/return

Sub Heal(int [COLOR=Lime]gm[/COLOR])
/squelch /target ${Group.Member[[COLOR=Lime]${gm}[/COLOR]]}
/delay 2
/casting "Heal Spell" gem1
/return
You can also nest /for, /next loops within each other:
Code:
Sub Main
/declare x int
/declare i int
/declare z int
/for z 1 to 20
    /for x 1 to 10
        /for i 30 downto 20 step 5
            /dothis
        /next i
    /next x
/next z
/return
Using MQ2Loops - MMOBugs Wiki, you can use /do and /while loops. These can be superior to standard loops for certain situations because the macro will stay where it is while or until certain conditions are met. Say you want to always heal yourself before doing anything else if you are below 50% hp, except save the tank if they fall below 10%:
Code:
Sub Main
:label
/while (${Me.PctHPs}<50) 
     /if (${Target.ID}!=${Me.ID}) /squelch /tar myself
     /if (${Me.SpellReady[heal spell name]}) /casting "heal spell name" gem1
     /if (${Group.MainTank.PctHPs}<10) /break
/endwhile
/call Heal
/goto :label
/return
While loops can be very useful for making sure certain things happen at certain times without having to go through a ton of other checks.


Do loops just perform something until a condition is met, say until i equals 10 in this example:
Code:
Sub Main
/declare i byte
/do
/varcalc i ${i}+1
/until (${i}==10)
/echo ${i}
/return
There are a few situations where this would be more useful than other loops and save you time or checks.

Then you can also use a combination. Such as /do a loop until i equals 10, but while i = 5, to /echo ${i}:
Code:
Sub Main
/declare i byte
/do
/varcalc i ${i}+1
/while (${i}==5)
    /echo ${i}
    /varcalc i ${i}+1
    /if (${i}==6) /continue
    /if (${i}==6) /break
/endwhile
/until (${i}==10)
/echo ${i}
/return
Then you could make the macro loop over that over and over again, /echoing 5, then 10:

Code:
Sub Main
/declare i byte
:label
/do
/varcalc i ${i}+1
/while (${i}==5)
    /echo ${i}
    /varcalc i ${i}+1
    /if (${i}==6) /continue
    /if (${i}==6) /break
/endwhile
/until (${i}==10)
/echo ${i}
/goto :label
/return
Loops are what make botting your toon possible. Mastering variables, loops, TLOs/members, and /if statements make it possible to automate anything that can be done manually in game.