Macros
Ideas for this page include sample macros and a manual in and of itself...
Maybe we could write a macro manual.... if we did it'd go something like this:
Macros are little snippets of text that allow a person to save typing or trigger complex actions with the click of a button or press of a key. Why would anyone want to use macros? Well when you have to type out /action smiles. for the 50th or 500th time you'll wish you could just type sm to smile. Or let's say you always equip your shield when you equip your sword. Normally you'd have to type /equip long sword. and then /equip wooden shield. With a macro you could simply press F7 and equip and/or — if you get fancy — unequip your sword and wooden shield.
Existing Macros
Before you start writing macros, check your default file for the premade ones included. Here are a few more common ones included by default:
"??" "/help " @text "\r" "aa" "/action " @text "\r" "gg" "/give " @text "\r" "ii" "/info " @text "\r" "kk" "/karma " @text "\r" "mm" "/money\r" "nn" "/news\r" "pp" "/ponder " @text "\r" "sh" "/share " @text "\r" "sl" "/sleep\r" "t" "/think " @text "\r" "tt" "/thinkto " @text "\r" "th" "/thank " @text "\r"
Custom Macros
Healers can try the Moonstone Functions macro in Moonstone Functions if you want to heal yourself easier. Now as of version 2.8.5 it automatically changes your icon to armed or unarmed and you can set Function keys (F9-F12) to heal specific exiles quickly.
If you're looking for Dynamic Share Cads where you automatically share a healer who is cadding you, you can get it here: Gorvin's Macros This macro is very useful on large hunts with more than 5 healers.
All Exiles with sunstones, suncrystals and sungems might want to get the tc_macro v2.5.8 to allow you to more conveniently use the devices easier. This macro uses a textlog scanner to work so you might wish to add the line: @login call GoScan to your default macros file.
People who want to plant Kudzu will want to check out the omega zu macro 4 (4.0b10 as of 20100318), available now. This is a beta, but on limited testing it works well. It also swaps bags but there is no longer a delay in planting as in version 3. It now detects if you didn't plant a seed because your inventory is full without looking at the text log. This macro is about as low latency as I you can get for the features.
Check out Macros Contributed for other macros people have made.
To install, see Installing Macros. For help in Troubleshooting macros see the linked page.
Types of Macros
There are three types of macros: text replacement macros that trigger while the person is still inputting text into the text box and, text expression macros that trigger when a person hits the return key and, key macros that trigger when one hits a key (or keys) .
Replacement Macros
Replacement macros are the simplest, and are defined by placing the text you want to trigger the replacement between single quotes, with the text you wish to have in replaced with in double quote after it. For instance 'zu' "Kudzu" would make it so anytime you typed the word "zu" it would be replaced in the text input box with "kudzu."
'zu' "kudzu" 'kb' "kitty beach"
Expression macros
Expression macros replace what you type in the text box with whatever text or actions you define. Once the return key is pressed the macro engine takes over and inputs what you specify within the body of the macro. This can be one line or many lines. These could be simple actions such as "wave" could be replaced with "/action waves and smiles at " @selplayer.name "\r" or it could be replaced with this slightly more complex macro:
"aa" { "/action prepares to do something dramatic!\r" pause 12 "/action " @text "\r" }
Using the above macro, let's say Manx typed out:
aa doesn't put the cap back on the toothpaste!
it would be replaces with this in the action text box:
Manx prepares to do something dramatic!
and 3 second later:
Manx doesn't put the cap back on the toothpaste!
Key Macros
Key macros are those that are triggered by hitting one of more keys. this can be:
option-shift-p "/useitem bag of kudzu seedlings 1\r"
to plant a kudzu seedling from a kudzu bag to
control-click "/pull " @click.name "\r" control-shift-click "/push " @click.name "\r"
or:
F3 { if @my.right_item == "Axe" "/equip Short Sword\r" else if @my.right_item == "Short Sword" "/equip Dagger\r" else if @my.right_item == "Dagger" "/equip chocolate\r" else if @my.right_item == "Chocolate" "/equip axe\r" end if }
Valid Trigger Keys
- examples included...
The function keys (F1 through F15)
- F3 call heal1
command-any key
- command-s "/pose sit \r"
option-any key (only on the Mac, not valid on windows)
- option-l "/pose lie \r"
control-any key
- control-d call dance1
alt-any key (only on windows?)
- alt-F3 "/equip dagger \r"
shift-any key (useful for windows - use with care, may have unintended consequences)
- shift-9 call plantzu
end
home
help
pageup
del
pagedown
click (this is a mouse button click)
control-click (right-click on the Mac)
click2 (right-click on Windows)
wheelup (wheel on a mouse, scrolling up)
wheeldown (wheel on a mouse, scrolling down)
The following work when NumLock is on. (Note: "*" and "/" do not work as valid triggers on the Windows.)
numpad-1 (or any number from 0-9, or */-+. on numpad)
- numpad-- call dance2
alt-numpad-1 (or any number from 0-9, or */-+. on numpad)
- alt-numpad-5 "/pose surp\r"
control-numpad-1 (or any number from 0-9, or */-+. on numpad)
- control-numpad-/ call dance3
- control numpad-6 call dance6
Macro Syntax
One of the toughest things to understand when learning a language is the proper syntax. For some reason a lot of manuals and books assume you'll pick up the proper syntax through example. However, this is hit and miss. If someone doesn't know why they are putting quote before some triggers and not others, they could very easily make the mistake of putting them in the wrong place, or not including them at all. So, before we delve into the real nitty gritty of Clan Lord's macro language, We will cover proper syntax, and a bit.
Triggers
First, things first: the macro language (and any almost every programming/scripting language out there) is structured to take the word or key press that triggers the action first then reads the commands that follow the trigger. For one command macros, you can simply put a few spaces between the trigger and the command:
"trigger" "command"
for macros with more than one command you have to put a return between each command because the macro language can only take one command per line. In order for the macro interpreter to know where one multi-line macro ends and another one starts, you have to put curly braces "{}" around all the lines of the macro, each on a line of its own:
"trigger" { command command command ... }
Where to use Quotes
Next quotes are used around two things in the macro language, and depending on where they are their meaning changes. either they are used around text that's intended as the trigger, or they are used around text you wish to have output to the text bubble/box over your character's head or output to your message sidebar, verbatim. That's why, for instance, if you want to be able to select a character (by command-clicking on them), let's say Andarius, and then simply type hi to say something, in addition to "Hi" you'd put quote around
"/hi"
and around
"hi " @selplayer.name ". Fancy weather we're having today.\r"
which outputs
hi Andarius. Fancy weather we're having today."
Note that the reserved variable @selplayer.name doesn't have quotes around it. This is because if it did, type it out as
"Hi @selplayer.name . Fancy weather we're having today.\r"
the output to your text bubble would look like this:
Hi @selplayer.name . Fancy weather we're having today.
Instead we terminate the quoted string with a quote after a space* and then use the \r that translates into a return, as if you hit the return key.
Also note that the \r must be within double quotes as in this example:
"wva" "Waves at " @selplayer.name "\r"
The importance of Spaces
- Without the space the output would read like this: HelloAndarius. Fancy weather we're having today. If that was a command instead of "hi," you would generate an error. i.e:
scripting:
"aa" "/action" @text "and looks smashing doing it.\r"
and typing aa eats a piece of bread would generate this error message in your message sidebar:
'ACTIONEATS' is not a recognized command.
Escape Sequences
An escape sequence is used to stop the macro interpreter from translating a character as a reserved character. For instance, the quote mark is reserved to define the start and end of literal text input or output from the CL client. But what if someone wants to macro something that has quote marks in the text output:
Macroing without an escape sequence before the quote mark
"/nnmacros" "you should read the scroll entitled "Macros" for all your macro questions.\r"
would generate this:
you should read the scroll entitled Macros"forallyourmacroquestions.
along with a quote mark in your text box. You should instead use this to generate the correct line of text:
"/nnmacros" "you should read the scroll entitled \"Macros\" for all your macro questions.\r"
which would output this: you should read the scroll entitled "Macros"for all your macro questions.
Also, if you precede a regular character with a backslash, the macro interpreter treats that character as a one of the special characters. \r is equivalent to pressing the return key. There are no other documented escape sequences for regular characters. {If someone has a list please send it to noivad.}
Variables
you declare variables before you first use them by using the set command. if a variable is declared inside a macro, that variable is local and is not passed to other macros or functions in the game. They only exist as long as the macro is running. Thus a simple loop for scanning the text log could have i meaning the counter while in another macro i could be used as a holder for the current inventory item. In the following example neither variable i interferes with each other because both are local to the macro they reside in:
"/loopmacro" { set i 0 set loopMax 60 label Loop if i< loopMax set i + 1 command... pause 40 command..." goto Loop end if } F3 { if @my.right.item != "moonstone" set i @my.right_item "/equip moonstone\r" "\use 5\r" pause 8 "/equip " i "\r" else "\use 5\r" end if }
Global variables are declared outside any macro or function and can be accessed by any macro or function once set. The only time a global variable is reset to its initial declared value is when the macros are reloaded which is each time you connect to the server and whenever you choose Reload Macros from the Options menu. For instance let's say you have a macro that constantly heals you when you have a moonstone equipped:
F4 { label Healingloop if @my.right_item == "moonstone" "/use \r" end if goto Healingloop }
However let's say you want to be able to active /sleep and not have you moonstone interrupt your sleep. All you would need to do is add a global variable declaration outside the /sleep and healing loop macros, and refer to it to see if you should use your moonstone. To set a global variable inside a macro you type setglobal <global variable name>:
set gSleepToggle 0
"sl" { "/sleep\r" setglobal gSleepToggle 1 }
F4 { setglobal gSleepToggle 0 label Healingloop if gSleepToggle == 0 if @my.right_item == "moonstone" "/use \r" end if end if pause 12 goto Healingloop }
Now whenever you enter sl your character will sleep and the gSleepToggle variable will be set to 1. When the healing loop looks checks to see if it should execute the commands it will see that the global variable is set to 1, and not execute the commands. However when you press F4 the gSleepToggle global variable will change back to 0 and the commands in the if statement will execute.
Reserved Variables
There are certain predefined variables in the macro language that allow you to get information from the client as to the current state of things in the game. A brief description of each follows:
@env Variables are variables with an @env prefix to control how macros execute.
- @env.echo (true/false) After any \r in a macro, replaces the text in the input box by the text that was sent by the macro. for instance if your trigger was /snooze and in the macro "/action Zzzzz....\r" was what was sent my the macro you set @env.echo to be true after you typed /snooze the text input box would have /action Zzzzz....\r. in it. if you do not define this variable in a macro the default @env.echo is false.
- @env.debug (true/false) gives extra information on macro loading and execution.This is useful when you're debugging a macro.
- @env.key_interrupts (true/false) If this is true a macro stops executing when a key is pressed. If it's false then a macro will keep executing while the person presses keys on the keyboard.
- @env.click_interrupts (true/false) stops executing a macro when you click in the graphics pane of the game window (where clicks move your character).
- @env.TextLog is the last line of text in the log window. Textlog scanning is a controversial command because of it's power. One could write a macro that scanned the text log every frame and if a certain phrase was present they could have their character do something in response to it.
@my Variables are variables that concern your character.
- @my.name is used to insert your name into a macro command, that way if you have multiple characters and you want one macro to make them do something with their name in it, or to check if they are the selected player you can include it in your default file instead of writing two duplicate macros:
if @sel.player_name == @my.name command command end if
- @my.simple_name is the same as @my.name except it has no spaces or punctuation.
- @my.right_item is the name of the item in your right hand.
- @my.left_item is the name of the item in your left hand.
- @my.forehead_item is the name of the item on your forehead.
- @my.[slot]_item is the name of the item in the slot named. (Torso, Legs, Shoulder, Neck, Finger, Hand, Head, etc.)
- @sel.player_simple.name is used to make the target of a macro the selected player. (You can select a player by command-clicking on them or by typing /select <name>.)
@click Variables are variables that concern what was clicked on in a CL Window.
- @click.name is the complete name of the exile clicked on in the Players Window.
- @click.simple_name is the name of the exile clicked on in the Players window, without spaces or punctuation.
Miscellaneous Variables
- @text is the complete text in the panel. For instance if you had a macro that was triggered by typing /set and you enter /set verbose on, then @text would equal verbose on.
- @textsel is the selected text in the panel. this works exactly the same as @text but it only includes the words highlighted.
- @first, @last, @prev, and @next are variables that represent player names on the current player list and can be used with the /select command. /select @first will select the first name in the player window, and /select @last will select the last name. If a player is already selected, /select @prev and /select @next will select the previous player and the next player on the list, respectively.
Arrays
Arrays are essentially lists of similar types of things. Instead of declaring a variable such as ExileOne, ExileTwo, etc to build a list, you declare and array Exile[1], Exile[2]. This makes handling list of things much easier because you can make a loop and advance the number that points to the entry in the list to advance to the next item to process that as well. You could even make a conditional statement that tests every item in the list and only activates on the ones you are looking for:
SomeMacro { set currentItem 0 label SCANLOOP if ItemList[currentItem] == "cloak" (do these commands) else (do these other commands) end if set currentItem + 1 if curentItem > (end of list) goto ENDOFMACRO else goto SCANLOOP end if label ENDOFMACRO }
Arrays are simple to make, basically you take a variable name (that is not reserved) and place the element number in the array in square brackets [x] that you wish to access. for instance, the following makes a list out of exiles entered after the macro trigger by looping through the list of words and then advancing the pointer by one. When the point is not less than or equal to the number of words, the loop is exited and a sidebar message informs you that the list is built. A concrete example of using this to make a shares list is in the Shares macro :
set ExileList "" "/adex" call buildExileList buildExileList { set pointer 0 label LOOPSTART setglobal ExileList[pointer] @text.word[pointer] set pointer + 1 if pointer <= @text.num_words goto LOOPSTART end if label END message "You list is built" }
if you added that to your macro file, reloade your macros or logged in and then tpye /adex Melben Creed Fiona Hoggle Natas and hit enter the macro would build a list with Melben as item 0 in the array and continuing until it reache Natas at the end of the list in position 4 in the array.
Also you could retrieve that list with another macro that stepped through each item and output it. This is a shorten, and simplified version of my share list macro. The full version is in Shares.mac:
/* ************************** shareMultiple v1.0.1 ************************** */ //<<m_shareMultiList>> (sh|/shm|shm <name> [name]) shares & adds to list shareMultiList { setglobal helpText[sh] "*'sh|/shm|shm <name> [name]' shares & adds exiles to list" setglobal count 0 if @text == "" if debug == 1 //debug flag for testing purposes message "No people listed. Defaulting to " listA //listA is defined elsewhere outside of this macro call BuildDefault //Build Default is essentially identical to LOOPB below, but with a static predefined list (listA) else "/share\r" end if else if @text == "?" message helpText[sh] else label LOOPB if count < @text.num_words "/share " @text.word[count] "\r" set count + 1 pause 20 goto LOOPB end if end if }
Conditional Arguments (If)
now macros that execute pre-planned actions are fine, but sometimes you want to add a little intelligent behavior to your macro. (In programming circles this is called Logic.) So, you use an if statement to test whether the following is true. If it is not the code between the end if, next else or else if statement is not executed.
If {this is true} {run this command set} Else if {that is true} {run that command set} Else {in the case that none are true} do this instead End if
An if statement can be as simple as 3 lines. The opening if test, the command you wish to execute if it is true and the closing end if statement to let the interpreter know the next statements are not conditional, and should be run no matter what. All If statements must include an end if at the end even if there are no lines following the if test.
If you want to test multiple conditions from the same information you use the else if statement. That way you don't have a lot of single "if ... end if statements and you don't run the risk of executing multiple commands that could contradict each other and give you unexpected results. For instance. Let's say you want a one key macro to swap your weapon with your purse. You would have to use an else if statement so you didn't test each case individually and always wind up with the second item listed equipped.
F4 { If @my.right_item == "dagger" "/equip purse\r" else if @my.right_item == "purse" "/equip dagger\r" end if }
Also if you have multiple else ifs that can evaluate to be true, only the first true result is parsed. So, if you ask if x is greater than 10 and then ask if x is greater than 20 and the number x equals 23, then only the first part of the if statement is executed. There are several ways aroundf this problem, but the easiest is to test the most restrictive condition first. This would be the proper way to write this macro:
if x > 20 <command 1> else if x > 10 <command 2> end if
Now when a number greater than 20 is compared, only command 1 is executed.
Notice there is no else statement in the above example. If you don't want anything to happen if nothing is true you can simply omit the else statment. The reason in this case is if you have something like your chain equipped and you accidentally hit F4, instead of your chain macro on F5, you don't unchain people you're trying to rescue.
Comparison Operators
A very common macro is a chain macro. But how do you test to see if you do not have your chain equipped? or how do you test if one number is greater than or less than another? or if a phrase appears in the text you're comparing. Simply use the proper operators.
- == means equal to
- != means not equal to
- < means less than or when you're comparing text it tests to see if the phrase is in the text.
- > means greater than
Loops
Loops are sections of code that you wish to repeat either a set number of times or infinitely. You may wish to scan the people clanning automatically when you log in for friend you can share with, and instead of typing
if characterName == myFriend "/share characterName\r" end if
over and over for each friend... you could start a loop by first labeling the part you want the loop to start at, and then at the end of the commands you want r-executed, you type goto and the name of the label.
label Start <command> <command> <...> Goto Start
The above loop will execute as long as the macros are allowed to run.* In order to control the number of time the loop repeats you include a counter, increment the counter inside the loop and test the counter to se if it is less than the maximum number of loops you wish to make. For instance let's say you want to info everyone playing in the game with one command.
set counter 0 set maxLoops 20 label Start if counter < maxLoops <command> <command> pause 1 set counter + 1 goto Start end if
*Note: There are certain snells, such as the jail and the hospital where all macros are stopped by the client. In those cases you want to put your macros that are called at login in another manually triggered macro.
Functions
functions look exactly like macros, except there are no quote around the trigger word. They are activated by other macros using the call command.
"sl" { "/sleep\r" setglobal sleepToggle 1 call function_name } function_name { pause 480 if sleepToggle == 1 "/ponder Zzzzz....\r" <commands...> end if }
The above example is very simple and not really deserving of a independent function, unless you want other ways to trigger your macro. For instance, in the following typing either /sleep or sl will accomplish the same goal:
"/sleep" call sleeper "sl" call sleeper
This is done so if you want to change your macros operation and there multiple triggers for it in your macros you only have to change one thing and not the same thing in multiple places.
Examples
Some of these Example Macros are taken directly from my macros.
control-click "/pull " @click.name "\r" //This is a right click on mac
control-option-click "/push " @click.name "\r"
Control-Click Select Exile Macro
control-click { $any_click "/select " @clicksplayer "\r" }
Equip/Unequip Right Macro
// A quick equip/unequip toggle macro set rsaveitem @my.right_item set defaultritem "greataxe" set ritem "" set debug 0 command-f2 { setglobal ritem @my.right_item if debug == 1 message "ritem is: " ritem end if if ritem == "nothing" "/equip " defaultritem "\r" else setglobal rsaveitem ritem "/unequip right\r" end if }
A Quick EPS Macro
//A Quick EPS macro "/eps" { setglobal ritem @my.right_item if debug == 1 message "ritem is: " ritem end if if ritem != "EtherealPortalStone" setglobal rsaveitem ritem "/equip etherealportalstone \r" if @text.word[0] == "check" message ***EPS Ready*** goto end end if end if "/use\r" label end }
A Quick Chaining Macro
//The QuickChain set rsaveitem "" f5 call flybychain wheelup call flybychain flybychain { if @my.right_item != "chain" setglobal rsaveitem @my.right_item "/equip chain\r" end if pause 1 "/use \r" }
Sleep Macro
"sl" { set @env.click_interrupts "true" setglobal sleepToggle 1 set count 0 "/pose lie\r" pause 5 "/action feels sleepy and takes a nap\r" pause 5 "/sleep\r" label start if count < 15 set count + 1 { if modeVerbose == 1 random "/ponder ZZzzzzz.......\r" or "/ponder zzzZZZZzzzz...\r" or "/ponder ...zzzzZZZZzzz\r" or "/ponder .z.z.z.Z.Z.z.z\r" or "/money\r" end random else random "/karma\r" or "/money\r" or call exilepicker pause 4 "/thank " @selplayer.simple_name "\r" end if } pause 500 goto start end if }
"u" "/use " @splayer "\r"
"uu" "/use " @splayer "\r"
"uuu" "/use " @splayer "\r"
"uuuu" "/use " @splayer "\r"
Blood Mage's Feign Death
click4 { $any_click "/useitem Bloodstone /dead \r" }
Set Variables Macro
This macro is a pretty simple macro, but gives you an idea of what more involved macros do. This is an actual macro that I use to remind myself of my own macro commands available (similar to clan lord's built in macro help, using the same syntax) as well as a short help message (activated by /set <command> ?) if needed. Note the use of the @text.word array (which is the automatically generated and filled with each word you enter in the text box. As always the first word after the trigger is actually zero 0 not 1.
"/set" //non-optimized to add: messageVariable and call InstructionMessages { if @text.word[0] == "?" message "*The following variables are available to set using this command:" pause 4 message "*verbose [0|1|?], longsleep [on|off|?]" pause 4 message "*clock, bag [(number)], maxbag (number), wh(isper) [on|off], debug [on|1|off|0]" else if @text.word[0] == "verbose" if @text.word[1] == "?" message "* This variable sets whether or not to associate an \"/action\" or \"/narrate\" with a macro." pause 16 message "\"1\" is on, \"0\" is off." if modeVerbose == 0 message "*Verbose mode is off." else message "*Verbose mode is on." end if else if @text.word[1] == 1 setglobal modeVerbose @text.word[1] else if @text.word[1] == 0 setglobal modeVerbose @text.word[1] else message "Error: Verbose Mode must be set to 0 or 1." goto end end if if modeVerbose == "1" message "*Verbose mode: On." else if modeVerbose == "0" message "*Verbose mode: Off." end if end if else if @text.word[0] == "debug" if @text.word[1] == "?" message "*This variable sets whether debigger messages are displayed when testing a macro." pause 12 if debug == 0 message "*debug is off." else message "*debug mode is on." end if else if @text.word[1] == 1 setglobal debug @text.word[1] else if @text.word[1] == 0 setglobal debug @text.word[1] else if @text.word[1] == "on" setglobal debug 1 else if @text.word[1] == "off" setglobal debug 0 else message "Error: Debug Mode must be set to 0 or 1, or off and on." goto end end if message "debug is set to: " debug else if @text.word[0] == "longsleep" if @text.word[1] == "?" message "*This variable is used to toggle the long sleep macro \"on\" and \"off\"." if longsleep == 0 message "*longsleep is off." else message "*longsleep is on." end if else if @text.word[1] == "off" setglobal longsleep 0 message "*longsleep is off." else if @text.word[1] == "on" setglobal longsleep 1 message "*longsleep is on." end if else if @text.word[0] == "bag" if @text.word[1] == "?" message "*This variable is used to select a bag number to use for kudzu planting." message "the current bag is " bag_number "." end if if @text.word[1] > 0 setglobal bag_number @text.word[1] "/selectitem \"bag of kudzu seedlings <#" bag_number "\"\r" message "the current bag is " bag_number "." end if else if @text.word[0] == "maxbag" if @text.word[1] == "?" message "*This variable is used to set how many kudzu bags you have on you." message "the number of kudzu bags is " max_bag "." end if if @text.word[1] > 0 setglobal max_bag @text.word[1] message "you number of kudzu bags is set at " max_bag "." end if end if label end }
Torin's Exile Picker Macro
// Pick a player at random. "/pick" call exilepicker exilepicker { set numPlayers 0 "/select " @last "\r" set lastExile @selplayer.simple_name "/select " @first "\r" set Exile[1] @selplayer.name if @selplayer.simple_name == lastExile set numPlayers 1 goto ranPicker end if set exileCounter 1 label exileCountLoop "/select " @next "\r" set exileCounter + 1 set Exile[exileCounter] @selplayer.name if @selplayer.simple_name == lastExile set numPlayers exileCounter goto ranPicker end if goto exileCountLoop label ranPicker set ranPick @random set ranPick % numPlayers set ranPick + 1 set randomExileClanning Exile[ranPick] "/select " randomExileClanning "\r" label endexilepicker }
Continued Reading
For more information on macros see the Macro Information section on the links page.
If you want to go straight to the GM’s mouth check out Official Macro Manual on the Tao Clan Lord Section
For more example macros and ideas for organizing your own, see Macros Contributed.
For help with errors or problems with macros, see Troubleshooting macros.