macro plugin: Difference between revisions

From OpenKore Wiki
Jump to navigation Jump to search
(Added as asked wrt http://forums.openkore.com/viewtopic.php?f=32&t=4868)
 
(201 intermediate revisions by 16 users not shown)
Line 1: Line 1:
With this plugin you can predefine command sequences (macros) which are run either manually or by situation-dependent triggers. The latest version is 2.0.3.
With this plugin you can predefine command sequences (macros) which are run either manually or by situation-dependent triggers. The latest version is 2.0.3.


== Instalation ==
== Installation ==
* Download your macro plugin. You can get it [http://bibian.ath.cx/?file=macro.tar.gz here] or from [[SVN]].
* <span style='color:red'>Note: As of migrating to Git, the macro plugin is included and enabled by default.</span>
* Go to your OpenKore main folder (the folder which contains the file openkore.pl) and create a subfolder called plugins, if there isn't already one.  
* Download the macro plugin. You can download it from the following links:
* Inside the Macro plugin's zipfile, you will find the file macro.pl and the folder Macro. Extract them to your plugins folder.  
** [http://sourceforge.net/p/openkore/code/HEAD/tree/plugins/macro/trunk/ '''trunk''' to use with OpenKore trunk (^/plugins/macro/trunk)]
* In your OpenKore control folder, create a blank file named macros.txt. In this file you will put your macros/automacros.  
** [http://sourceforge.net/p/openkore/code/HEAD/tree/plugins/macro/branches/openkore-2.0.7/ '''2.0.7-compatible branch''' to use with OpenKore 2.0.7 (^/plugins/macro/branches/openkore-2.0.7)]
or checkout it from SVN:
svn co svn://svn.code.sf.net/p/openkore/code/plugins/macro/trunk/ macro
* Go to your OpenKore main folder (''the folder which contains the file openkore.pl'') and create a subfolder called '''plugins''', if there isn't already one. Create a subfolder '''macro''' in it.
* Inside the Macro plugin's zipfile, you will find the file '''macro.pl''', the folder '''Macro''' and other files and folders. Extract them to your plugins/macro folder, so '''macro.pl''' ends up in plugins/macro/macro.pl.
* In your OpenKore control folder, create a blank file named '''macros.txt'''. In this file you will put your macros/automacros.  


After installation, your OpenKore file tree should look like this (ignoring openkore's own files):  
 
After installation, your OpenKore file tree should look like this (''ignoring OpenKore's own files''):  
  openkore
  openkore
  |-- openkore.pl
  |-- openkore.pl
Line 15: Line 21:
  |-- logs
  |-- logs
  |-- plugins
  |-- plugins
  |  |-- Macro
  |  |-- macro
  |  |  |-- Automacro.pm
|  |-- Macro
  |  |  |-- Data.pm
  |  |  |-- Automacro.pm
  |  |  |-- Parser.pm
  |  |  |-- Data.pm
  |  |  |-- Script.pm
  |  |  |-- Parser.pm
  |  |  |-- Utilities.pm
  |  |  |-- Script.pm
  |  |-- macro.pl
  |  |  |-- Utilities.pm
  |  |-- macro.pl
  |-- src
  |-- src
  |-- tables
  |-- tables


 
== Console Commands ==
== Commands ==
=== Syntax ===
=== Syntax ===
  macro <macroname> [options] [-- parameter(s)]
  macro <macroname> [options] [-- parameter(s)]


Runs macro <macroname>.
Runs macro <macroname>.
Line 78: Line 85:


; macro stop
; macro stop
: Stop current macro.
: Stops current macro.


; macro pause
; macro pause
Line 87: Line 94:


; macro version
; macro version
: Print version number.
: Prints version number.


; macro reset [<name(s)>]
; macro reset [<''name''(''s'')>]
: Resets all run-once automacros or the specified automacro <name>.
: Resets all run-once automacros or the specified automacro <''name''>.


; macro status
; macro status
: Shows whether or not a macro is currently running. If that's the case it shows the delay for the next command, the current line, overrideAI setting, whether or not it has finished and whether or not the macro registered to AI queue.
: Shows whether or not a macro is currently running. If that's the case it shows the delay for the next command, the current line, overrideAI setting, whether or not it has finished and whether or not the macro registered to AI queue.


; macro varstack
: Show all variables used by the macro plugin ([[macro_plugin#Special_Variables|special]] or [[macro_plugin#Variable_declaration_and_usage|custom]]).


== Configuration files ==
== Configuration files ==
Line 108: Line 117:
|-style=background-color:#F9F9F9;
|-style=background-color:#F9F9F9;
!Option
!Option
! Value
!Value
!Default
!Description
!Description
|-
|-
|align=center| macro_nowarn
|align=center| macro_nowarn
|align=center| 0 <br/> 1
|align=center| [[boolean]]
|align=center| 0
|align=center| enable or disable the annoying warnings when not using call in your automacro(s)  
|align=center| enable or disable the annoying warnings when not using call in your automacro(s)  
|-
|-
|align=center| macro_orphans
|align=center| macro_orphans
|align=center| terminate <br/> reregister <br/> reregister_safe
|align=center| terminate <br/> reregister <br/> reregister_safe
|aling=center| terminate
|align=center| override openkore's AI  
|align=center| override openkore's AI  
|-
|-
|align=center| macro_file  
|align=center| macro_file  
|align=center| file name
|align=center| file name
|align=center| file containing the macros ("macros.txt" by default)
|aling=center| macros.txt
|align=center| file containing the macros
|-
|-
|align=center| macro_allowDebug  
|align=center| macro_allowDebug  
|align=center| 0 <br/> 1
|align=center| [[boolean]]
|align=center| console-check also processes openkore's debug messages (default: 0). Warning: slows down the plugin.  
|aling=center| 0
|align=center| console-check also processes openkore's debug messages. Warning: slows down the plugin.  
|}
|}


== Macro Syntax ==
== Macro Syntax ==
Line 135: Line 148:
     yattayatta..
     yattayatta..
  }
  }
* You can use any name you want for your macro. Be careful not to make two macros with the same name.
* Macros is executed from top to bottom.
* Only macro instructions can be used in macros. If you need to use console command, use '''do'''.


=== Macro Instructions ===


You can use any name you want for your macro. Becareful to not make two macros with the same name.
; do <''command''>
All the [[Console Commands]] can be used in macros.
: Run <''command''>, as if it was entered in OpenKore terminal. Commands are from [[Console Commands]].
 
; do <command>
: Run <command>. Commands from [[Console Commands]].


  macro foo {
  macro foo {
Line 149: Line 163:
  }
  }


<span style="color:red">The command ''ai clear'' is disabled by default in the plugin.</span style><br>
<span style="color:red">If has a macro the command ''do ai manual'' or ''do ai off'' , the macro will stop its execution.</span style>


; log <text>
 
: Prints a text in the console.
; log <''text''>
: Prints a text in the console. Can contain macro $variables and @stuff.


  macro foo {
  macro foo {
Line 159: Line 176:




; pause [<n>]  
; pause [<''n''>]  
: Pauses the macro for '''n''' seconds.
: Pauses the macro for '''n''' seconds.


Line 165: Line 182:
     log It's 10:00:00
     log It's 10:00:00
     pause 10
     pause 10
     log Not it's 10:00:10  
     log Now it's 10:00:10  
     log 10 seconds have passes after the first print.
     log 10 seconds have passed after the first print.
  }
  }


<span style="color:red">''pause'' not only pauses the macro running, pauses all our character's actions.</span style>


; call <macroname> [<n>]
: Calls macro <macroname> [<n> times]. When <macroname> is finished the current macro continues.


; release (<name> | all)
; call <''macroname''> [<''n''>]
: Calls macro <''macroname''> [<''n''> times]. When <''macroname''> is finished the current macro continues.
 
 
; release (<''name''> | all)
: Reenables a locked automacro ("run-once" keyword or locked by "lock") or reenables all automacros when using release all.
: Reenables a locked automacro ("run-once" keyword or locked by "lock") or reenables all automacros when using release all.


; lock (<name> | all)
 
: Locks an automacro and disables it's checks. To locks all automacros, use ''lock all''.
; lock (<''name''> | all)
: Locks an automacro and disables it's checks. To lock all automacros, use ''lock all''.
 


; stop
; stop
: Immediately terminates the running macro.
: Immediately terminates the running macro.


; set <option> <value>
 
; set <''option''> <''value''>
: Sets macro features:
: Sets macro features:
:* orphan method
:* orphan method
:* macro_delay timeou
:* macro_delay timeout
:* overrideAI [0|1]
:* overrideAI [0|1]
:* repeat times
:* repeat times
:* exclusive [0|1]
:* exclusive [0|1]


=== Variable declaration and usage ===
You can work with variables. Variable declaration is not needed. All macro variables are global.
Set a value of variable:
$variable = value


=== Variable declaration and usage ===
Get a value of variable (note that this is "Macro Syntax" section, so it doesn't apply to automacro conditions, config options etc):
You can define and work with own variables.
$variable
To set a variable use ''$variable = value'', to recall the value use ''$variable''.


  macro Hello {
  macro Hello {
     $var = Hello
     $var = Hello
     $var2 = Word!
     $var1 = World!
     log $var $var1
     log $var $var1
  }
  }
Line 203: Line 231:
This would print in the console:
This would print in the console:


  [log] Hello Word!
  [macro] Hello World!
 


If you want to use the '''''$''''' symbol you should escape it with '''''\'''''.
If you want to use the '''''$''''' symbol you should escape it with '''''\'''''.
Line 218: Line 247:
     log Counter is at $counter
     log Counter is at $counter
     $counter++
     $counter++
     log Not it's $counter
     log Now it's $counter
     $counter--
     $counter--
     log It's back to $counter
     log It's back to $counter
Line 224: Line 253:


The result is:
The result is:
  log Counter is at 0
  [macro] Counter is at 0
  log Now it's 1
  [macro] Now it's 1
  log It's back to 0
  [macro] It's back to 0


 
You can also unset/vanish the existing variable using ''''undef'''' or ''''unset'''':
For calculation use ''''' @eval '''''.  
$x = 1
log \$x is $x
$x = undef  # or you can use 'unset'
log \$x now vanished: $x
Variable assignment doesn't evaluate, it only does macro substitutions, so use @eval for calculations.


  macro math {
  macro math {
Line 235: Line 269:
     $num2 = 3
     $num2 = 3
     $result = @eval($num+$num2)
     $result = @eval($num+$num2)
     log $num + $num2 = $result
     log sum of $num and $num2 is $result
  }
  }


The result is:
The result is:
  log 2 + 3 = 5
  [macro] sum of 2 and 3 is 5
 
You can also use @eval inside @eval
 
macro math {
    $num = 2
    $num2 = 3
    $result = @eval(@eval($num+$num2) - @eval($num+$num2))
    log ($num + $num2) - ($num + $num2) = $result
}
 
{| class="wikitable" border="1" cellspacing="0"
|-style=background-color:#F9F9F9;
! Operator
! Description
|-
|align=center| +
|align=center| Plus
|-
|align=center| -
|align=center| Minus
|-
|align=center| *
|align=center| Times
|-
|align=center| /
|align=center| Divided
|}
 


You can extract the first element from a comma-separated list using like this:
You can extract the first element from a comma-separated list using like this:
Line 278: Line 284:
  }
  }


The result it:
The result is:
  log The first element from \$list is Banana
  [macro] The first element from \$list is banana
  log \$list contains apple, strawberry, grape
  [macro] Now $list contains apple, strawberry, grape
 
 
'''''Note.''''' Variable names can only contain letters and numbers,.


'''''Note:''''' Variable names may only contain letters and numbers.


== Nested Variables ==
== Nested Variables ==
Line 291: Line 295:
  macro foo {
  macro foo {
     $name = Camila
     $name = Camila
     ${$name} = Brazil
     ${$name} = Brazil   # Note: ${name} is equal to $Camila now
     log $name lives on ${$name}
     log $name lives on ${$name}
  }
  }
Line 297: Line 301:
The result is:
The result is:


  log Camila lives on Brazil
  [macro] Camila lives on Brazil
 


== Special Variables ==  
== Special Variables ==  
There are special readonly variables which begin with a dot. They are pre-defined with the macro plugin.
There are special read-only variables which begin with a dot. They are pre-defined with the macro plugin.


:* $.map - the map you're on ("prontera")
:* $.map - the map you're on ("prontera")
Line 307: Line 310:
:* $.time - current time as unix timestamp ("1131116304")
:* $.time - current time as unix timestamp ("1131116304")
:* $.datetime - current date and time ("Fri Nov 4 15:59:36 2005")
:* $.datetime - current date and time ("Fri Nov 4 15:59:36 2005")
:* $.hour - current hour time in 24h format
:* $.minute - current minute time
:* $.second - current second time
:* $.hp - current hp
:* $.hp - current hp
:* $.sp - current sp
:* $.sp - current sp
Line 314: Line 320:
:* $.zeny - current amount of zeny
:* $.zeny - current amount of zeny
:* $.status - current statuses in a comma-separated list
:* $.status - current statuses in a comma-separated list
:* $.paramN - command line parameters (see commands)
:* $.paramN - command line parameters (see [[Macro_plugin#Syntax|Syntax]])
:* $.caller - name of the last triggered automacro
:* $.caller - name of the last triggered automacro
:* $.weight - return the current weight of the character
:* $.weight - returns the current weight of the character
:* $.maxweight - return the maximum weight of the character  
:* $.maxweight - returns the maximum weight of the character
 
== Special Keywords ==


These keywords (having common form of '''@<keyword>(<arguments>)''' each) interpolated to corresponding values just about anywhere inside ''macro blocks'' (except '''goto''', '''end''', '''$var=[$list]''', LHS of '''${$var}''' assignment, label definitions, macro name in '''call''', '''set'''). Macro variables can be used in arguments, with the exception for <code>@nick()</code>.


== Special Keywords ==
; @npc (<''x''> <''y''> | /[http://www.regular-expressions.info/quickstart.html regexp]/i | "<''name''>")
They are used to return values depending on the parameters given.
: Return NPC's index which location is <''x''> <''y''> or NPC's name match regexp or NPC's name is equal to <''name''> . Returns -1 if no NPC was found.  


; @npc (<x> <y> | /regexp/i | "<name>")
; @inventory (<''item''>)
: Return NPC's ID which location is <x> <y> or NPC's name match regexp is equal to <name> . If no NPC is found returns -1.  
: Returns inventory item index of <''item''>. If <''item''> doesn't exist, it returns -1.


; @inventory (<item>)
; @Inventory (<''item''>)
: Returns Inventory Item ID of <item>. If <item> doens't exist returns -1.
: Same as '''@inventory''' but returns all matching indexes as a comma-separated list or -1 if the item was not found.  


; @Inventory (<item>)
; @invamount (<''item''>)
: Same as @inventory but returns all matching IDs as a comma-separated list or -1 if the item was not found
: Returns the amount of the given <''item''> in inventory.


; @invamount (<item>)
; @cart (<''item''>)
: Returns the amount of the given <item> in inventory.
: Returns cart item index of <''item''>. If <''item''> doesn't exist, it returns -1.


; @cart (<item>)
; @Cart (<''item''>)
: Returns Cart Item ID of <item>. If <item> doesn't exist returns -1.
: Same as '''@cart''' but returns all matching indexes as a comma-separated list or -1 if the item was not found.


; @Cart (<item>)
; @cartamount (<''item''>)
: Same as @cart but returns all matching IDs as a comma-separated list or -1 if the item was not found.
: Returns the amount of the given <''item''> in cart.


;@cartamount (<item>)
; @storage (<''item''>)
: Returns the amount of the given <item> in cart.
: Returns storage item index of <''item''>. If <''item''> doesn't exist, it returns -1.


; @storage (<item>)
; @Storage (<''item''>)
: Returns Storage Item ID of <item>. If <item> doesn't exit returns -1.
: Same as '''@storage''' but returns all matching indexes as a comma-separated list or -1 if the item was not found.


; @Storage (<item>)
; @storamount (<''item''>)
: Same as @storage but returns all matching IDs as a comma-separated list or -1 if the item was not found.
: Returns the amount of the given <''item''> in storage.  


; @storamount (<item>)
; @player (<''name''>)
: Returns the amount of the given <item> in storage
: Returns player index of player <''name''>. If player <''name''> is not found, it returns -1.


; @player (<name>)
; @monster (<''name|ID''>)
: Returns player ID of player <name>. If player <name> is not found, returns -1.
: Returns monster index of monster <''name|ID''>. If monster <''name|ID''> is not found, it returns -1.


; @vender (<name>)
; @vender (<''name''>)
: Returns Vender ID of vender <name>. If vender <name> is not found, returns -1.
: Returns vender index of vender <''name''>. If vender <''name''> is not found, it returns -1.


; @store (<name>)
; @store (<''name''>)
: Looks for an item in a store and returns ID or -1 if the item was not found.
: Looks for an item in a store and returns index or -1 if the item was not found.


; @shopamount (<item>)
; @shopamount (<''item''>)
: Returns the amount of the given <item> in shop.
: Returns the amount of the given <''item''> in shop.


; @random ("<argument1>", "<argument2>"....)
; @random ("<''argument1''>", "<''argument2''>", ...)
: Returns randomly one of the given arguments .
: Returns randomly one of the given arguments .


; @rand (<n>, <m>)
; @rand (<''n''>, <''m''>)
: Returns a random number between (and including) <n> and <m>.
: Returns a random number between (and including) <''n''> and <''m''>.


; @eval (<argument>)
; @eval (<''argument''>)
: Evaluates the given <argument>.
: Evaluates the given <''argument''>. '''Contents of @eval is Perl and does NOT have macro plugin syntax, except for variables and @() substitution.'''


; @arg ("<argument>", <n>)
; @arg ("<''argument''>", <''n''>)
: Returns the <n>th word of <argument> or an empty string if the word index is out of range.
: Returns the <''n''>th word of <''argument''> or an empty string if the word index is out of range.
'''''Note.''''' <n> can also be a variable.


; @config (<variable>)
; @config (<''variable''>)
: Returns the value of <variable> specified in config.txt.
: Returns the value of <''variable''> specified in config.txt.


; @venderitem (<name>)
; @venderitem (<''name''>)
: Looks for an item in a players shop and returns ID or -1 if the item was not found.  
: Looks for an item in a player's shop and returns index or -1 if the item was not found.  


; @venderprice (<indexID>)
; @venderprice (<''indexID''>)
: Looks for an item in a players shop and returns its price.
: Looks for an item in a player's shop and returns its price.  
 
; @nick (<word>)
: Escape all the regexp characters with '''\'''.


; @nick (<''word''>)
: Escapes all the regexp metacharacters and some of the perl special characters with '''\''' (a backslash). Especially for player's name.


== Chaining commands ==
== Chaining commands ==
You can run multiple commands one after another without having to wait for openkore's ai or macro_delay or whatever. Just enclose these commands with '''[''' and ''']'''.   
You can run multiple commands one after another without having to wait for openkore's AI or macro_delay or whatever. Just enclose these commands with '''[''' and ''']'''.   


  0 macro foo {
  0 macro foo {
Line 405: Line 412:
  9 }
  9 }


Line 3 starts the chaining mode. This line has no delay. Lines 4, 5 and 6 are run as soon as the previous command has finished with no delay and they cannot be interrupted. Line 7 stops the chaining mode and line 8 will be run $macro_delay seconds after that.  
Line 3 starts the chaining mode. This line has no delay. Lines 4, 5 and 6 are run as soon as the previous command has finished with no delay and they cannot be interrupted. Line 7 stops the chaining mode and line 8 will be run $macro_delay seconds after that.
 


== Sub-lines ==
== Sub-lines ==
Instanted of using one command/var assigments per line, you can separate them using ''';'''.
Instead of using one command/var assignments per line, you can separate them using a semi-colon "''';'''". There were few syntaxes that you could use it to minimize the lines with this function such as Var Assignment ($var), Double/Nested Var (${$var}), Increase(++) or Decrease(--) Var or Double/Nested Var, Set, Lock, Release, Log (Use separate line if you want to use ";" in the log message), Pause, Do and Perl Sub-Routine Macro Command. <s>Further info:  [http://forums.openkore.com/viewtopic.php?f=32&t=966 ''Forums'']</s>


  macro foo {
  macro foo {
     $i = 1;pause 5; log \$i = $i;$ii = 2 ; $iii = 3; $i++; $ii--; lock automacroName; release automacroName; set overrideAI 1
     $i = 1; pause 5; log \$i = $i; $ii = 2; $iii = 3; $i++; $ii--; lock automacroName; release automacroName; set overrideAI 1
  }
  }


Commands separated by ''';''' will have no delay between them, just like the Chaining commands, unless on commands '''pause and log'''.
Commands separated by a semi-colon "''';'''" will have no delay between them, just like the Chaining commands, unless on certain commands such as '''pause and log'''.
 


== Conditions ==
== Conditions ==
Line 432: Line 437:
|align=center| less or equal to
|align=center| less or equal to
|-
|-
|align=center| ==
|align=center| == or =
|align=center| equal to
|align=center| equal to <s>([http://forums.openkore.com/viewtopic.php?f=32&t=9042&p=37947&#p37947 link])</s>
|-
|-
|align=center| >
|align=center| >
Line 445: Line 450:
|-
|-
|align=center| ~
|align=center| ~
|align=center| <left part> is element of <right part (comma-separated list)>  
|align=center| <left part> is element of <right part (''comma-separated list'')>  
|-
|-
|align=center| =~
|align=center| =~
|align=center| Check for regexp
|align=center| <left part> matches [http://perldoc.perl.org/perlre.html regular expression] <right part>
<s>Further Discussion: [http://forums.openkore.com/viewtopic.php?f=32&t=966&start=73 ''Forums'']</s>
|-
|-
|align=center| arg .. arg2
|align=center| arg .. arg2
Line 454: Line 460:
|}
|}


== Flow control and labels ==
Just as prevalent in high-level languages ​​constructs like "''if .. else''", "''while''", "''foreach''", "''for .. next''", "''do .. while''" and other commands, the macro plugin also has some "if", "else", "elsif", "'''switch'''", "'''case, "goto" and "while".. Since there are no (''visible'') line numbers, you'll need to use labels which can be defined by a colon followed by the name of the label.
=== Syntax of command conditions ===
==== If ====
Right now, macro ''if'' conditions are very close to the perl ''if'' statements. It accepts unlimited number of statements in just one ''if'' condition line, regexp matching is allowed (unfortunately, no backreference using parenthesis) and the use of '''&&''' for '''and''' meaning and '''||''' for '''or''' meaning.
You can use a simple statement;
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2) (goto <label> | call <macro> [<n>] | stop | { )
A simple statement with '''OR''' condition;
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 || arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) (goto <label> | call <macro> <n> | stop | { )
A simple statement with '''AND''' condition;
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 && arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) (goto <label> | call <macro> <n> | stop | { )
Or with both;
if ((arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 || arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) && arg5 <[[Macro plugin#Conditions|Conditions]]> arg6) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 && arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) || arg5 <[[Macro plugin#Conditions|Conditions]]> arg6) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 && arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) || (arg5 <[[Macro plugin#Conditions|Conditions]]> arg6 && arg7 <[[Macro plugin#Conditions|Conditions]]> arg8)) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 || arg3 <[[Macro plugin#Conditions|Conditions]]> arg4) && (arg5 <condition> arg6 || arg7 <condition> arg8)) (goto <label> | call <macro> <n> | stop | { )
:* Notice inside of each brackets containing the '''AND''' and '''OR''' symbols.
Where;
:* ''arg'' can be a variable, a nested variable, a special keyword, @eval, letters and numbers or even a [[Macro plugin#Perl_Subroutines|Perl Subroutines]] function.
:* <''label''>, the name of an existing label, can only contain letters and numbers
:* <''macro''>, the name of a existing macro, and
:* <''n''>, the number of times it will call the macro.
:* < ''{'' >, the beginning of a block of commands that will be executed if the condition is true, to finish it, use ''}''.
'''''Note:'''''
:* If ''<n>'' is defined as '''0''' or even if it is '''undefined''', the called macro name will run at once and then stop, not continuing the previous macro caller at a run time.
:* If ''<n>'' is greater than '''0''', the called macro name will run at '''n''' time/s and then continuing the previous macro caller line at a run time.
'''''Note²:''''' If statements are unlimited, you can use as many statements as you want.
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2 || arg3 <[[Macro plugin#Conditions|Conditions]]> arg4 || ... || arg'''''N''''' <[[Macro plugin#Conditions|Conditions]]> arg'''''N+1''''') (goto <label> | call <macro> <n> | stop)
Where;
:* ''arg'' can be a variable, a nested variable, a special keyword, @eval or letters and numbers.
:* All the conditions is up to the '''''N'''''th argument <[[Macro plugin#Conditions|Conditions]]> '''''N+1'''''th argument.
:* While '''''N''''' is an integer number/s which is greater than zero.
===== Postfix control =====
Another way to use the ''if'' is to put it at the end of the command.
<command> if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2)
The command before the ''if'' statement will only be executed if the condition is true.
The way to create the condition is the same as previously explained.
In this example below, the two commands are equivalent:
call buy if ($.zeny > 1000)
if ($.zeny > 1000) call buy
==== Else ====
In case ''if'' is used with open braces ( { ), it's possible to use the command '''else''' along closed braces (  } ) in the end of the command block ''if'', to start a new command block that will be executed if ''if'' is false.
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2) {
    command1
    command2
    ...
    commandN
} else {
    command1
    command2
    ...
    commandN
}
In this case, the first command block will be executed if the condition turns out being true. If it's false, the second command block (preceding by '''else''') will be executed.


== Flow control and labels ==  
'''''Note:''''' Inside braces you're allowed to use tons of commands.<br>
While all high level programming languages have constructs like "if .. else", "while", "foreach", "for .. next", "do .. while" and function calls their common denominators are "if", "goto" and "while". That's why the macro plugin only supports these three keywords. Since there are no (visible) line numbers you'll need to use labels which can be defined by a colon followed by the name of the label.
'''''Note:''''' It is not compulsory to wear '''else'''.
 
==== Elsif ====
It is like the else + if. The '''elsif''' is in the same place ''else'' with the difference being added conditions to its command block is executed if its conditions are true.


=== If statement ===
if (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2) {
Macro if conditions is very close to the perl if statements. It accepts unlimited number of statements in just one if conditions, regexp matching (unfortunately, no backreference using parenthesis) and the use of '''&&''' for '''and''' meaning and '''||''' for '''or''' meaning.
    command1
==== Syntax ====
    command2
You can use a simple statement
    ...
  if (arg <condition> arg2) (goto <label> | call <macro> [<n>])
    comandoN
  } elsif (arg1 <[[Macro plugin#Conditions|Conditions]]> arg2) {
    command1
    command2
    ...
    commandN
} else {
    command1
    command2
    ...
    commandN
}


A statement with OR condition
In this case, the first block of commands will be executed if the condition is true. If it is false, the second block of commands (preceded by the '''elsif''') will be executed if its condition is true. Should also be false, the command blocks ''else'' to be executed.
if (arg <condition> arg2 || arg3 <condition> arg4) (goto <label> | call <macro> <n>)


A statement with AND condition
'''''Note:''''' As in the ''else'', inside braces of '''elsif''' you're allowed to use tons of commands.<br>
if (arg <condition> arg2 && arg3 <condition> arg4) (goto <label> | call <macro> <n>)
'''''Note:''''' It is not compulsory to wear the ''else'' or '''elsif'''.


Or with both
==== Switch/case ====
if (arg <condition> arg2 || arg3 <condition> arg4 && arg5 <condition> arg6) (goto <label> | call <macro> <n>)
It's similar to ''if'' followed by enumerous ''elsif''. It's useful to leave the code cleaner when the paramater being analyzed is the same, this way you don't need to repeat it.


Where ''arg'' can be a variable, a nested variable, a special keyword, @eval or letters and numbers. ''<label>'' the name of a existing label, can only contain letters and numbers , ''<macro>'' the name of a existing macro and ''<n>'' the number of times it will call the macro.
switch (arg1) {
Conditions can be found on [[Macro plugin#Conditions|Conditions]].
    case (<[[Macro plugin#Conditions|Conditions]]> arg2) (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
    case (<[[Macro plugin#Conditions|Conditions]]> arg2) (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
    ...
    else (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
}


'''''Note.''''' if ''<n>'' is '''not defined''' or it's '''0''' it will call the macro once and stop, not continuing the first macro. If it's '''1''' it will run the called macro and continue the first macro. If it's bigger than '''1''' it will call the macro '''n'''' times and continue the first macro.
'''''Note:''''' The use of ''else'' is optional.
'''''Note:''''' In case that no ''case'' ends being true, ''else'' will be activate if it exists.


==== Examples ====
==== Examples ====
This macro will go a random walk
macro walk {
    $num = @rand(1, 4)
    if ($num == 1) {
        do c I will follow the path 1
        do north
    }
    if ($num == 2) {
        do c I will follow the path 2
        do south
    }
    if ($num == 3) {
        do c I will follow the path 3
        do east
    }
    if ($num == 4) {
        do c I will follow the path 4
        do west
    }
}
Simplified version of the above macro, using postfix control.
macro walk {
    $num = @rand(1, 4)
    do c I will follow the path $num
    do north if ($num == 1)
    do south if ($num == 2)
    do east  if ($num == 3)
    do west  if ($num == 4)
}
The following macro will tell if you've over 1.000z or 1.000z or less.
macro checkZeny {
    if ($.zeny > 1000) {
      do c I've over 1.000z!
    } else {
      do c I've 1.000z or less...
    }
}
A little more complete than the above macro. Informa has more than 1.000z, has exactly 1.000z or has less than 1.000z with emoticons
macro checkZeny {
    if ($.zeny > 1000) {
      do c I've over 1.000z!
      do e money
    } elsif ($.zeny == 1000) {
      do c I have exactly 1.000z.
      do e !
    } else {
      do c I've 1.000z or less...
      do e panic
    }
}
Similar to the above but with a different syntax
macro checkZeny {
    switch ($.zeny) {
        case (> 1000) {
            do c I've over 1.000z!
            do e money
        }
        case (== 1000) {
            do c I have exactly 1.000z.
            do e !
        }
        else {
            do c I've 1.000z or less...
            do e panic
        }
    }
}
The below macro will print '''\$num is 1''' if $num == 1, '''\$num is 2''' if $num == 2, '''\$num is 3''' if $num == 3.
  macro checknum {
  macro checknum {
     $num = @rand(1, 3)
     $num = @rand(1, 3)
Line 487: Line 660:
     log \$num is 1
     log \$num is 1
     stop
     stop
     :one
     :two
     log \$num is 2
     log \$num is 2
     stop
     stop
     :one
     :three
     log \$num is 3
     log \$num is 3
     stop
     stop
  }
  }


The above macro will print '''\$num is 1''' if $num == 1, '''\$num is 2''' if $num == 2, '''\$num is 3''' if $num == 3.
The above macro can be written using '''call''' instead of '''goto'''.
The same macro can be written using '''call''' instanted of '''goto'''.


  macro checknum {
  macro checknum {
Line 529: Line 701:
  }
  }


 
=== WHILE Loop ===
=== While Loop ===
A ''while'' in macros means that certain commands will be ran while the defined conditions are met.
A while in macros means that certain commands will be ran while the defined conditions are met.
==== Syntax ====
==== Syntax ====
  while (arg <condition> arg) as <loop>
  while (arg <condition> arg) as <loop>
Line 538: Line 709:
  end <loop>
  end <loop>


Where ''arg'' can be a variable, a nested variable, a special keyword, @eval or letters and numbers. And <loop> the name of the loop. You can give any name you want.
Where;
:* ''arg'' can be a variable, a nested variable, a special keyword, @eval or letters and numbers. And  
:* <loop> is the name of the loop. You can give any name you want.
Conditions can be found on [[Macro plugin#Conditions|Conditions]].
Conditions can be found on [[Macro plugin#Conditions|Conditions]].


==== Examples ====
==== Examples ====
Line 552: Line 724:


The result is
The result is
  log \$i = 0
  [macro] \$i = 0
  log \$i = 1
  [macro] \$i = 1
  log \$i = 2
  [macro] \$i = 2
  log \$i = 3
  [macro] \$i = 3
  log \$i = 4
  [macro] \$i = 4
  log \$i = 5
  [macro] \$i = 5
  log \$i = 6
  [macro] \$i = 6
  log \$i = 7
  [macro] \$i = 7
  log \$i = 8
  [macro] \$i = 8
  log \$i = 9
  [macro] \$i = 9
 
== Automacros ==
 
At this point, you can define macros and call them, typing 'macro <macro name>' in OpenKore's console input.


Automacro is an automatic trigger for calling your macros, just like blocks in config.txt are automatic triggers for certain AI actions.


== Automacros ==
Automacro block consists of:
Automacros are macro which automatically trigger when certain conditions are met, just like how blocks in config.txt trigger depending on the conditions set in them.
* one and only one automacro '''call''' option (this is different from macro '''call''' instruction) or automacro '''call''' block
* any number of automacro conditions, which will limit automatic triggering to situation where all of them are true
 
Automacro does not trigger if there is currently running macro in exclusive mode. Otherwise, automacro clears macro queue (which means that all currently running macros are stopped) before call.


The point of automacros is that you use them to check for conditions. When the condition is fulfilled you can either respond to it in the automacro or call a macro to do it.
The point of automacro is that you use them to check for certain conditions and call your macro when all conditions are fulfilled.


=== Syntax ===
=== Syntax ===
  automacro <name> {
With '''call''' option, macro name must be provided:
    <conditions> <values>
  automacro <automacro name> {
    call <macro>
<automacro conditions (and only them)>
call myMacro
}
macro myMacro {
<macro instructions (and only them, as this is regular macro)>
# for example:
do move prontera
do move payon
  }
  }


Or you can omit the '''call <macro>'''
With '''call''' block, you can omit standalone macro definition, and put macro instructions inside that block (it still defines a normal macro, just semi-anonymous):
  automacro <name> {
  automacro <automacro name> {
    <condition> <values>
<automacro conditions (and only them)>
    call {
call {
          <commands>
<macro instructions (and only them, as this is regular macro)>
        }
# for example:
do move prontera
do move payon
}
  }
  }


The first syntax is usefull if you want more than one automacro to call one simple macro.
Two examples above do the same thing.
 
 
 
The first syntax is useful if you want more than one automacro to call one simple macro.
  automacro First {
  automacro First {
    <conditions>  
<conditions>  
    call print
call print
  }
  }
  automacro Seconds {
automacro Second {
    <conditions>  
<conditions>  
    call print
call print
  }
  }
  macro print {
  macro print {
    log $.caller triggered
log $.caller triggered
  }
  }


=== Conditions ===
=== Conditions ===
An Automacro needs to have at least 1 conditions or it will loop forever. It can have as many conditions you want, also some conditions automatically sets some variables so you can use them in your macros.


; map <mapname>
''Event''-based conditions only have a chance to be true (to trigger) once per corresponding event fire. Maximum of one of these conditions is allowed (otherwise, only one will be used anyway). If you use one of these conditions, you can think of it as a main trigger condition for your automacro.
: Triggers when your current map is <mapname>.  


; location [not] <mapname [<x1> <y1> [<x2> <y2>]] [, ...]
''State''-based conditions are true (and able to trigger) as long as corresponding data meets the condition. Usually, any number of these conditions is allowed.
: Triggers when you are [not] at the specified location.
When neither <x1> <y1> nor <x2> <y2> are given it triggers when you are [not] on <mapname>.
When <x2> <y2> are not given it triggers when you are [not] on <mapname> at (<x1>,<x2>).  
When both <x1> <y1> and <x2> <y2> are defined it triggers when you are on <mapname> somewhere between <x1>, <y1> (upper left) and <x2>, <y2> (lower right, where <x1> < <x2> and <y1> > <y2>
Comma-separated arguments are treated as OR conditions:


location geffen, prontera 123 234
Developer notes for state-based conditions: multiple instances of these conditions SHOULD only be true when all of them are true (that is, as in "AND" operator). When comma-separated list of values is used, condition SHOULD be true when ANY of values apply.


Triggers when you are either in geffen or in prontera at 123 234.
Consequences:
* if automacro with '''console'''-based condition can't trigger when corresponding event fired for some reason (other conditions are not met OR blocked by exclusive macro), it will forget about event this time
* automacro with only '''hp'''-based conditions (or without any conditions) which calls macro without exclusive mode AND without run-once will probably enter endless loop of triggering (until environment changes by other means than macro plugin)


Multiple lines are treated as AND conditions:
Some conditions set some special variables which usually contain useful information, you can use them in your macros.


location not geffen
==== Events ====
location not prontera


Triggers when you are neither in geffen nor in prontera.
; hook <''hookname''>
: Triggers when openkore calls <''hookname''>.
; save <''hash key''> (use in combination with hook)
: Saves the value of <''hash key''> in a variable '''$.hooksaveN''' (N starts from 0). Currently, hooks work in macros only as automacro condition. There is no easy other way to check it from a macro.


automacro hook {
    hook packet_privMsg
    save MsgUser
    save Msg
    call {
          log Player $.hooksave0 said $.hooksave1
          }
}


; mapchange (<mapname>|any) [, ...]
; console ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i])
:Triggers when changing map to <mapname>. If the argument is any then it triggers on any map change.  
: Triggers when <''text''> is received on console or the text received matches <regexp>.  
Comma-separated arguments are treated as OR conditions.  
: The ''i'' switch means the regexp is case-insensitive.


:'''Set variables''':
:* $.lastLogMsg - the console text that trigerred the automacro.
:* $.lastMatchN - backreference from regexp.


; hp <[[Macro plugin#Conditions|condition]]> <amount>[%]
:<span style="color:red;">Console generally includes custom strings, like names and chat messages, which can match your regexp, so matching the beginning and the end of the string should almost always be used (learn about regexp to find out how to do it - it's not just matching symbols ''at'' the beginning/end). Furthermore, use as narrow character classes as possible (avoid using <code>.*</code> and alike). Otherwise, your macro may be exploited.</span>
: Triggers when your hp matches the defined condition.
Multiple lines are treated as AND Conditions.


:Furthermore, format of console output isn't frozen and can change in future versions and/or in translations. It's not a good idea to rely on '''console''' condition.


; sp <[[Macro plugin#Conditions|condition]]> <amount>[%]
; spell [party] <''spell''> [, ...]
: Triggers when your sp matches the defined condition.  
: Triggers when someone casts <''spell''> on you or you are in its scope.
Multiple lines are treated as AND conditions.  
: Party support syntax is included (''optional''). Also trigger on party member (''as a target'') by other party or monster. '''Ex''': "spell party Lord of Vermilion"
: Comma-separated arguments are treated as '''OR''' conditions.


:'''Set variables''':
:* $.caster - returns the actor (''by player/monster'') which triggered the last spell syntax on you/party member/in range spell.
:* $.casterName - returns the name of player/monster which triggered the last spell syntax on you/party member/in range spell.
:* $.casterID - returns the ID of player/monster which triggered the last spell syntax on you/party member/in range spell.
:* $.casterPos - returns the last position of player/monster which triggered the last spell syntax on you/party member/in range spell (''x y'').
:* $.casterSkill - returns the skill name which triggered the last spell syntax on you/party member/in range spell.
:* $.casterTarget - returns the "location of spell" which triggered the last spell syntax on you/party member/in range spell.
:* $.casterTargetName - returns the name of the party member that is in the range of the triggered (''as a target'') last spell syntax.
:* $.casterDist - returns the distance (''between the caster and YOU'') which triggered the last spell syntax on you/party member/in range sp


; spirit <[[Macro plugin#Conditions|condition]]> <amount>
: Triggers when your spirits match <condition> <amount>.
Multiple lines are treated as AND conditions.


; pm ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i]) [, <''player''>]
: Triggers when <''text''> is received by PM [from <''player''>] or the text received matches <regexp>.
: The ''i'' switch means the regexp is case-insensitive.


; weight <[[Macro plugin#Conditions|condition]]> <amount>[%]
:'''Set variables''':
: Triggers when your weight matches <condition> <amount> (absolute value) or <condition> <amount> percent (relative value).  
:* $.lastpm - the name of the player who PMed you
Multiple lines are treated as AND conditions.  
:* $.lastpmMsg - the message




; cartweight <<[[Macro plugin#Conditions|condition]]>> <amount>[%]
; pubm ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i]) [, <''distance''>]
: Triggers when your cart weight matches <condition> <amount> (absolute value) or <condition> <amount> percent (relative value).
: Triggers when a public message [within a distance of <''distance''>] is received and it is <''text''> or matches <regexp>  
Multiple lines are treated as AND conditions.  
: The ''i'' switch means the regexp is case-insensitive.


:'''Set variables''':
:* $.lastpub - player's name
:* $.lastpubMsg - player's message


; zeny <[[Macro plugin#Conditions|condition]]> <amount>
: Triggers when your zeny amount matches <condition> <amount>.
Multiple lines are treated as AND conditions.


; party ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i])
: Triggers when <''text''> is received by party chat or the text received matches <regexp>.
: The ''i'' switch means the regexp is case-insensitive.


; soldout <condition> <slots>
:'''Set variable'''s:
: Triggers when the amount of sold out item slots in your shop matches <condition> <slots>.  
:* $.lastparty - player's name
Multiple lines are treated as AND conditions.  
:* $.lastpartyMsg - player's message


; guild ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i])
: Triggers when <''text''> is received by guild chat or the text received matches <regexp>.
: The ''i'' switch means the regexp is case-insensitive.
:'''Set variables''':
:* $.lastguild - player's name
:* $.lastguildMsg - player's message


; status [not] <status> [, ...]
:Triggers when you are [not] <status>.
The statuses "dead" and "muted" are supported additionally.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; system ("<''text''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i])
: Triggered by system chat.
:* $.lastsysMsg - message


; inventory "<item>" <[[Macro plugin#Conditions|condition]]> <amount> [, ...]
Triggers when you have <condition> <amount> of <item> in your inventory.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; mapchange ( <''mapname''> | any | * ) [, ...]
: Triggers when changing map to <''mapname''>. If the argument is '''any''' or '''*''' (asterisk or star) then it triggers on any map change.
: Comma-separated arguments are treated as '''OR''' conditions.


; storage "<item>" <[[Macro plugin#Conditions|condition]]> <amount> [, ...]
: Triggers when you have <condition> <amount> of <item> in your storage.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; playerguild (<''guild list''> [, ...] | <''guild.txt''>) ['', <distance>'']
: Trigers when guild list (guild1, guild2, ..., guild'''''N''''') or lists of guilds inside control/guild.txt matches player's guild name when '''charNameUpdate''' or '''player''' hook-on-demand packets is received. <s>Further discussion could be found at [http://forums.openkore.com/viewtopic.php?f=32&t=6417 ''Forums'']</s>.
: If <''distance''> is not defined, [[clientSight]] value will be considered.
: Comma-separated guild list are treated as '''OR''' conditions.
:'''Set variables''':
:* $.lastPlayerID - return the account ID of the player that trigger the syntax
:* $.lastGuildName - return the guild name of the player that trigger the syntax
:* $.lastGuildNameBinID - return the ID of the player that trigger the syntax
:* $.lastGuildNameBinIDDist - return the distance(from you) of the player that trigger the syntax
:* $.lastGuildNameBinIDName - return the player's name that trigger the syntax
:* $.lastGuildNameBinIDJobName - return the job class of the player that trigger the syntax


; cart "<item>" <[[Macro plugin#Conditions|condition]]> <amount> [, ...]
: Triggers when you have <condition> <amount> of <item> in your cart.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; areaSpell <''spell''> [<''distance''>]
: Triggers when someone starts casting a ground-targeted skill (centre) that is in the scope of the distance condition from you. If <''distance''> is not defined, it will consider [[clientSight]] value.
: Comma-separated arguments are treated as '''OR''' conditions.
:'''Set variables''':
:* $.areaName - return the spell area effect name
:* $.areaActor - return source actor(Player, Monster and etc etc)
:* $.areaSourceName - return the name of the actor
:* $.areaSourceID - return the binID of the actor
:* $.areaPos - return the centre position/location of the area spell (ex: '''123 123 payon''')
:* $.areaDist - return the distance of the spell area (centre) to your char
: <s>'''Note.''' [http://forums.openkore.com/viewtopic.php?f=32&t=6444 areaSpell automacro syntax Macro 2.0.3-SVN].</s>


; shop "<item>" <[[Macro plugin#Conditions|condition]]> <amount> [, ...]
==== States ====
: Triggers when you have <condition> <amount> of <item> in your shop.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; map <''mapname''>
: Triggers when your current map is <''mapname''>.
: May be used only once per automacro block.
: TODO: treat comma-separated arguments as OR condition?


; base <condition> <level>
: Triggers when your baselevel matches <condition> <level>.
Multiple lines are treated as AND conditions.


; location [not] <''mapname'' [<''x1''> <''y1''> [<''x2''> <''y2''>]] [, ...]
: Triggers when you are [not] at the specified location.
: When neither <''x1''> <''y1''> nor <''x2''> <''y2''> are given, it triggers when you are [not] on <''mapname''>.
: When <''x2''> <''y2''> are not given, it triggers when you are [not] on <''mapname''> at (<''x1''>,<''y1''>).
: When both <''x1''> <''y1''> and <''x2''> <''y2''> are defined it triggers when you are on <''mapname''> somewhere between <''x1''>, <''y1''> (upper left) and <''x2''>, <''y2''> (lower right, where <''x1''> < <''x2''> and <''y1''> > <''y2''>
: Comma-separated arguments are treated as '''OR''' conditions:


; job <[[Macro plugin#Conditions|condition]]> <level>
location geffen, prontera 123 234
: Triggers when your job level matches <condition> <level>.
Multiple lines are treated as AND conditions.


:Triggers when you are either in geffen or in prontera at 123 234.


; class <job>
:Multiple lines are treated as '''AND''' conditions:
: Triggers when your job is <job>


location not geffen
location not prontera


; spell [party] <spell> [, ...]
:Triggers when you are neither in geffen nor in prontera.
: Triggers when someone casts <spell> on you or you are in it's scope.
Party support syntax is included (optional). Also trigger on party member(as a target) by other party or monster. Ex:
"spell party Lord of Vermilion"
Comma-separated arguments are treated as OR conditions.  


Set variables:
* $.caster - Return the actor (by player/monster) which triggered the last spell syntax on you/party member/inrange spell.
* $.casterName - Return the name of player/monster which triggered the last spell syntax on you/party member/inrange spell.
* $.casterID - Return the ID of player/monster which triggered the last spell syntax on you/party member/inrange spell.
* $.casterPos - Return the last position of player/monster which triggered the last spell syntax on you/party member/inrange spell (x y).
* $.casterSkill - Return the skill name which triggered the last spell syntax on you/party member/inrange spell.
* $.casterTarget - Return the "location of spell" which triggered the last spell syntax on you/party member/inrange spell.
* $.casterTargetName - Return the name of the party member that is in the range of the triggered(as a target) last spell syntax.
* $.casterDist - Return the distance (between the caster and YOU) which triggered the last spell syntax on you/party member/inrange sp


; hp <[[Macro plugin#Conditions|condition]]> <''amount''>[%]
: Triggers when your hp matches the defined condition.
: Multiple lines are treated as '''AND''' Conditions.


; monster [not] <monster(s) name> <[[Macro plugin#Conditions|condition]]> [<distance>]
: Triggers when <monster(s) name> is near. If <distance> is not set, it will consider the value from [[Config.txt#clientSight|clientSight]].


Set variables:
; sp <[[Macro plugin#Conditions|condition]]> <''amount''>[%]
* $.lastMonster - Name of the last monster that triggered the automacro.
: Triggers when your sp matches the defined condition.  
* $.lastMonsterPos - triggered monster position. Ex: x, y map_name
: Multiple lines are treated as '''AND''' conditions.  
* $.lastMonsterDist - distance between the triggered monster and you.
* $.lastMonsterID - the ID of the triggered monster.
* $.lastMonsterCount - risk point (Avaible only for monster syntax)


Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; spirit <[[Macro plugin#Conditions|condition]]> <''amount''>
: Triggers when your spirits match <condition> <''amount''>.
: Multiple lines are treated as '''AND''' conditions.


; aggressives <[[Macro plugin#Conditions|condition]]>  <number>
: Triggers at <number> of aggressives.
Multiple lines are treated as AND conditions.


; weight <[[Macro plugin#Conditions|condition]]> <''amount''>[%]
: Triggers when your weight matches <''condition''> <''amount''> (absolute value) or <''condition''> <''amount''> percent (relative value).
: Multiple lines are treated as '''AND''' conditions.


; player ("<playername>" | /<regexp>/[i]) [, <distance> ]
: Triggers when <playername> is on screen and not more than <distance> blocks away.
Multiple lines are treated as AND conditions.


; cartweight <[[Macro plugin#Conditions|condition]]> <''amount''>[%]
: Triggers when your cart weight matches <''condition''> <''amount''> (absolute value) or <''condition''> <''amount''> percent (relative value).
: Multiple lines are treated as '''AND''' conditions.


; equipped [<slot>] (<item> | none) [, ...]
: Triggers when <item> or none is equipped [in slot <slot>]
Slots are topHead, midHead, lowHead, leftHand, rightHand, robe, armor, shoes, leftAccessory, rightAccessory and arrow.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


; zeny <[[Macro plugin#Conditions|condition]]> <''amount''>
: Triggers when your zeny amount matches <''condition''> <''amount''>.
: Multiple lines are treated as '''AND''' conditions.


; var <variable> (unset | <[[Macro plugin#Conditions|condition]]> <value>)
: Triggers when <variable> is either unset or matches <condition> <value>.
Multiple lines are treated as AND conditions.


; soldout <[[Macro plugin#Conditions|condition]]> <''slots''>
: Triggers when the amount of sold out item slots in your shop matches <''condition''> <''slots''>.
: Multiple lines are treated as '''AND''' conditions.


; varvar <nested variable> (unset | <condition> <value>)
: Triggers when <nested variable> is either unset or matches <condition> <value>.
Multiple lines are treated as AND conditions.


; status [not] <''status''> [, ...]
: Triggers when you are [not] <''status''>.
: The statuses "'''dead'''" and "'''muted'''" are supported additionally.
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.


; console ("<text>" | /<regexp>/[i])
: Triggers when <text> is received on console or the text received matches <regexp>.
The ''i'' switch means the regexp is case insensitive.


Set variables:
; inventory "<''item''>" <[[Macro plugin#Conditions|condition]]> <''amount''> [, ...]
* $.lastLogMsg - the console text that trigerred the automacro.
: Triggers when you have <''condition''> <''amount''> of <''item''> in your inventory.
: Comma-separated arguments are treated as '''OR''' conditions.  
: Multiple lines are treated as '''AND''' conditions.  




; pm ("<text>" | /<regexp>/[i]) [, <player>]
; storage "<''item''>" <[[Macro plugin#Conditions|condition]]> <''amount''> [, ...]
: Triggers when <text> is received by pm [from <player>] or the text received matches <regexp>.  
: Triggers when you have <''condition''> <''amount''> of <''item''> in your storage.  
The ''i'' switch means the regexp is case insensitive.  
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.  


Set variables:
* $.lastpm - the name of the player who pmed you
* $.lastpmMsg - the message


; cart "<''item''>" <[[Macro plugin#Conditions|condition]]> <''amount''> [, ...]
: Triggers when you have <''condition''> <''amount''> of <''item''> in your cart.
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.


; pubm ("<text>" | /<regexp>/[i]) [, <distance>]
: Triggers when a public message [within a distance of <distance>] is received and it is <text> or matches <regexp>
The ''i'' switch means the regexp is case insensitive.


Set variables:
; shop "<''item''>" <[[Macro plugin#Conditions|condition]]> <''amount''> [, ...]
* $.lastpub - player's name
: Triggers when you have <''condition''> <''amount''> of <''item''> in your shop.
* $.lastpubMsg - player's message
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.  




; party ("<text>" | /<regexp>/[i])
; base <[[Macro plugin#Conditions|condition]]> <''level''>
: Triggers when <text> is received by partychat or the text received matches <regexp>.  
: Triggers when your base level matches <''condition''> <''level''>.  
The ''i'' switch means the regexp is case insensitive.  
: Multiple lines are treated as '''AND''' conditions.  


Set variables:
* $.lastparty - player's name
* $.lastpartyMsg - player's message


; job <[[Macro plugin#Conditions|condition]]> <''level''>
; guild ("<text>" | /<regexp>/[i])
: Triggers when your job level matches <condition> <''level''>.  
: Triggers when <text> is received by guildchat or the text received matches <regexp>.  
: Multiple lines are treated as '''AND''' conditions.  
The ''i'' switch means the regexp is case insensitive.  


Set variables:
# $.lastguild - player's name
# $.lastguildMsg - player's message


; class <''job''>
: Triggers when your job is <''job''>
: '''Example''' class Glt. Cross
: May be used only once per automacro block.
: TODO: treat comma-separated arguments as OR condition?


; hook <hookname>
: Triggers when openkore calls <hookname>.


; monster [not] <''monster(s) name''> [<[[Macro plugin#Conditions|condition]]> <''distance''>]
: Triggers when <''monster(s) name''> is near. If <''distance''> is not set, it will consider the value from [[clientSight]].
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.


; save <hash key> (use in combination with hook)
:'''Set variables''':
: Saves the value of <hash key> in a variable $.hooksaveN
:* $.lastMonster - name of the last monster that triggered the automacro.
:* $.lastMonsterPos - triggered monster position. '''Ex''': x, y map_name
:* $.lastMonsterDist - distance between the triggered monster and you.
:* $.lastMonsterID - the [[ml|index]] of the triggered monster.
:* $.lastMonsterBinID - the ID of the triggered monster.
:* $.lastMonsterCount - risk point (''available only for monster syntax'')


automacro hook {
    hook packet_privMsg
    save MsgUser
    save Msg
    call {
          log Player $.hooksave1 said $.hooksave2
          }
}


; notMonster <''monster(s) name''> [<[[Macro plugin#Conditions|condition]]> <''distance''>]
: Equivalent to ''monster [not]''.
: May be used only once per automacro block. TODO: Why, when '''monster''' may be used several times? Why at all, when there is '''monster not'''?


; whenGround [not] <spell>
: Triggers when we are in the effect of ground status.
Comma-separated list will be treated as OR condition


; aggressives <[[Macro plugin#Conditions|condition]]>  <''number''>
: Triggers at <''number''> of aggressives.
: Multiple lines are treated as '''AND''' conditions.


; areaSpell <spell> [<distance>]
: Triggers when someone use an area of spell(centre) that is in the scope of the distance condition from you. If <distance> is not defined, it will consider [[Config.txt#clientSight|clientSight]] value.
Comma-separated arguments are treated as OR conditions.


; player ("<''player name''>" | /<[http://www.regular-expressions.info/quickstart.html regexp]>/[i]) [, <''distance''> ]
: Triggers when <''player name''> is on screen and not more than <''distance''> blocks away.
: Multiple lines are treated as '''AND''' conditions.
:'''Set variables''':
:* $.lastPlayerName - name of the last player that triggered the automacro.
:* $.lastPlayerPos - triggered player position. '''Ex''': x, y map_name
:* $.lastPlayerLevel - base level of the last player that triggered the automacro.
:* $.lastPlayerJob - job of the last player that triggered the automacro.
:* $.lastPlayerAccountId - account id of the last player that triggered the automacro.
:* $.lastPlayerBinId - [[pl|binID/index]] of the last player that triggered the automacro.


; localtime <[[Macro plugin#Conditions|condition]]> <time>
: Triggers when defined the conditions for <time> mets.
Time is on 24h format.


; equipped [<''slot''>] (<''item''> | none) [, [<slot>] (<item> | none) [, ...]]
: Triggers when <''item''> or none is equipped [in slot <''slot''>]
: Slots are '''topHead''', '''midHead''', '''lowHead''', '''leftHand''', '''rightHand''', '''robe''', '''armor''', '''shoes''', '''leftAccessory''', '''rightAccessory''' and '''arrow'''.
: Each comma-separated argument only checks for its own slot, if it's specified.
: Comma-separated arguments are treated as '''OR''' conditions.
: Multiple lines are treated as '''AND''' conditions.


; run-once (0 | 1)
: When set to '''1''' the automacro will be locked after being triggered.
Use the macro command '''release''' to unlock this automacro.


; var <''variable''> (unset | <[[Macro plugin#Conditions|condition]]> <''value''>)
: Triggers when <''variable''> is either unset or matches <''condition''> <''value''>.
: Multiple lines are treated as '''AND''' conditions.
: '''Note:''' in this parameter specifies ONLY the name of the variable, without the "$" symbol. Example:
automacro variable {
  var test unset
  timeout 5
  call {
    log test is unset: $test
  }
}
macro set_test {
  $test = 1
  log variable \$test = $test
}


; overrideAI (0 | 1)
:When set to '''1''' the macro ignores openkore's AI. This means it won't pause upon "move" or "status dead".


; varvar <''nested variable''> (unset | <[[Macro plugin#Conditions|condition]]> <''value''>)
: Triggers when <''nested variable''> is either unset or matches <''condition''> <''value''>.
: Multiple lines are treated as '''AND''' conditions.


; delay <n>
: Waits for <n> seconds before calling the corresponding macro.


; whenGround [not] <''spell''>
: Triggers when we are in the effect of ground status.
: May be used only once per automacro block. TODO: allow multiple '''whenGround''' lines per automacro, to check whether several different ground statuses are affecting?
: Comma-separated list will be treated as '''OR''' condition


; timeout <n>
: Wait at least for <n> seconds before this automacro can be triggered again.


; localtime <[[Macro plugin#Conditions|condition]]> <''time''>
: Triggers when CPU clock time matches the given <[[Macro plugin#Conditions|condition]]> <''time''>.
: Time is on 24h format. Ex: 11:22:33 (hour:minute:second)
: Multiple lines are treated as '''AND''' conditions.


; macro_delay <n>
: Overrides the global macro delay setting for the called macro.


; eval <perl expression>
: Triggers when <perl expression> is true.
: May be used only once per automacro block. TODO: allow multiple '''eval''' lines per automacro?


; priority <num>
==== Special ====
: Choose which automacros should be checked before others. The smaller <num> is is the sooner the automacro gets checked. If priority is not given, the priority is assumed to be '''0'''. (zero: check first).


Other stuff you can write in automacro block. Use maximum of one for each of these, unless specified otherwise. These may be NOT conditions.


; overrideAI (0 | 1)
; macro_delay <''n''>
; exclusive (0 | 1)
; exclusive (0 | 1)
: Automacros which have exclusive set cannot be interrupted by other automacros.  
; orphan <''method''>
: Already documented options for macro call.
: Each may be used only once per automacro block.
 
; run-once (0 | 1)
: When set to '''1''' the automacro will be locked after being triggered.
: Use the macro command '''release''' to unlock this automacro.
: May be used only once per automacro block.


; disabled <[[boolean]]>
: Automacro will be initially locked.
: Use the macro command '''release''' to unlock this automacro.
: May be used only once per automacro block.


; set <variable> <value>
; delay <''n''>
: Sets variable <variable> to <value>. You can have multiple set lines per automacro.  
: Waits for <''n''> seconds before calling the corresponding macro.
: May be used only once per automacro block.


; timeout <''n''>
: Waits at least for <''n''> seconds before this automacro can be triggered again.
: May be used only once per automacro block.


; orphan <method>
; priority <''num''>
:Sets the method of how to deal with [[Macro plugin#Orphaned Macros|orphaned macros]].
: Chooses which automacros should be checked before others. The smaller the <''num''> is, the sooner the automacro gets checked. If priority is not given, the priority is assumed to be '''0'''. ('''zero''': ''check first'').
: May be used only once per automacro block.


; set <''variable''> <''value''>
: Sets variable <''variable''> to <''value''>. You can have multiple set lines per automacro.


=== Example ===
=== Example ===
Line 886: Line 1,155:
  }
  }


Always remember, when opening a '''{''', always close it with '''}'''.


Always remember, when opening a '''{''', always close it with a '''}'''.


== Orphaned Macros ==
== Orphaned Macros ==
It may happen - for example by using ''ai clear'' while a macro is running - that a macro becomes orphaned. That means the macro object exists but cannot continue because it requires the AI queue to contain the entry "macro" (or "deal") at the first place. When the AI queue gets cleared, the "macro" entry vanishes.
It may happen - for example by using ''ai clear'' while a macro is running - that a macro becomes orphaned. That means the macro object exists but cannot continue because it requires the AI queue to contain the entry "macro" (or "deal"<!-- deal? WTF? -->) at the first place. When the AI queue gets cleared, the "macro" entry vanishes.
There are three methods:  
There are three methods:  


Line 897: Line 1,166:
|-
|-
|align=center| terminate  
|align=center| terminate  
|align=center| terminates the macro (equivalent to macro stop)  
|align=center| terminates the macro (''equivalent to macro stop'')  
|-
|-
|align=center| reregister  
|align=center| reregister  
Line 905: Line 1,174:
|align=center| re-registers to AI queue when AI gets idle. This means the macro will continue when all other tasks are done.  
|align=center| re-registers to AI queue when AI gets idle. This means the macro will continue when all other tasks are done.  
|}
|}


== Perl Subroutines ==
== Perl Subroutines ==
Macro plugin support perl subroutines in macros. Uou can now use perl without all the limitations from '''eval''' command.
Macro plugin support Perl subroutines in macros. You can now create any simple Perl function without limitations from the '''eval''' command.  


Example No.1 - how to create a Perl Subroutine function in Macro
  macro sub {
  macro sub {
   $list = Orange, Milk, Soya, Peach
   $list = Orange, Milk, Soya, Peach
Line 919: Line 1,188:
   log \$x = $x  # $x here is 1
   log \$x = $x  # $x here is 1
  }
  }
  sub existsInList {
  sub existsInList {
   my ($list, $val) = @_;
   my ($list, $val) = @_;
Line 929: Line 1,197:
       s/\s+$//;
       s/\s+$//;
       s/\s+/ /g;
       s/\s+/ /g;
       next if ($_ eq "");
       next if $_ eq "";
       return 1 if (lc($_) eq $val);
       return 1 if lc eq $val;
   }
   }
   return 0;
   return 0;
  }
  }


Example No.2 - how to create a re-writable file function using Perl Subroutine in Macro
automacro confHP1 {
    hp > 85%
    exclusive 1
    run-once 1
    set setting Demon Pungus  #becareful on your case, its case sensitive
    set attack 1
    set teleport 0
    set telesearch 1
    call HP
}
automacro confHP2 {
    hp < 75%
    exclusive 1
    run-once 1
    set setting Demon Pungus
    set attack 1
    set teleport 2
    set telesearch 1
    call HP
}
macro HP {
      #Getting the value of the $setting monster name Ex: $setting $exist1 $exist2 $exist3
        $exist1 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{attack_auto}:"None")
        $exist2 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_auto}:"None")
        $exist3 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_search}:"None")
        log Old Values are $setting $exist1 $exist2 $exist3
        log Changing the values to $setting $attack $teleport $telesearch
        do eval Misc::mon_control("$::Macro::Data::varStack{setting}")->{attack_auto} = $attack; Misc::mon_control("$::Macro::Data::varStack{setting}")->{teleport_auto} = $teleport; Misc::mon_control("$::Macro::Data::varStack{setting}")->{teleport_search} = $telesearch
        log Writting mon_control.txt with new values
        rewrite()  # see the sub-routine function below
        log Reloading mon_control.txt
        do reload mon_control
        $exist1 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{attack_auto}:"None")
        $exist2 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_auto}:"None")
        $exist3 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_search}:"None")
        log New mon_control.txt Setting: $setting $exist1 $exist2 $exist3
        log Macro done
    #if $teleport = 0 ; means the Higher automacro HP is currently triggered
    #if $teleport = 2 ; means the Lower automacro HP is currently triggered
        if ($teleport < 2) goto releaseHighHp
        :releaseLowHp
            release confHP1
            stop
        :releaseHighHp
            release confHP2
            stop
}
sub rewrite {
  my $monster = Misc::mon_control("$::Macro::Data::varStack{setting}");
  my @lines = ();
  if (open(FILE, "<:utf8", Settings::getControlFilename("mon_control.txt"))) {
      while (<FILE>) {
        s/\x{FEFF}//g; chomp;
        if (/^#/ || /^\n/ || /^\r/) {
            push @lines,$_;
            next
        }
        /^(\d+|([a-zA-Z' ]+)*) -?\d/;
        if ("$::Macro::Data::varStack{setting}" eq $1 && defined $monster) {
            $_ = $1; s/\s+$//;
            push @lines,$_ . " $monster->{attack_auto} $monster->{teleport_auto} $monster->{teleport_search} $monster->{attack_lvl} $monster->{attack_jlvl} $monster->{attack_hp} $monster->{attack_sp} $monster->{weight}"
        }
        else {push @lines,$_}
      }
      close FILE
  }
  open(FILE, ">:utf8", Settings::getControlFilename("mon_control.txt"));
  print FILE join "\n", @lines;
  close FILE;
}


== Comments ==  
== Comments ==  
The macro files allow comments, i.e. lines that are ignored by the macro plugin. Lines starting with a # will be treated as comment.
The macro files allow comments, i.e. lines that are ignored by the macro plugin.
:* Lines starting with a # will be treated as a comment.
:* Everything after a space and followed by a # will also be ignored.
  macro happy {
  macro happy {
       # this is a comment line
       # this is a comment line
     log I'm Happy
     log I'm Happy # this is also a comment
  }
  }


Will print:
Will print:


  [log] I'm Happy
  [macro] I'm Happy


== Examples with Explanations ==
== Examples with Explanations ==


I assume you already know how to use console commands and understand how they work. If not, before going further read through all console commands and try out ones like a, ai, move, cart get, storage add, talk, deal, take, direction commands, sl, sm, relog, pm and others.
I assume you already know how to use [[Console Commands]] and understand how they work. If not, before going further, read through all [[Console Commands]] and try out ones like ''a'', ''ai'', ''move'', ''cart get'', ''storage add'', ''talk'', ''deal'', ''take'', direction commands, ''sl'', ''sm'', ''relog'', ''pm'' and others.


Keep another page with this manual open for cross reference.
Keep another page with this manual open for cross reference.


Okay, so there are 2 types of macros
Okay, so there are 2 types of macros
-automacros – these trigger automatically
#'''automacros''' – these trigger automatically
-macros – these do not trigger automatically but need to be called manually or by an automacro
#'''macros''' – these do not trigger automatically but need to be called manually or by an automacro
 


'''Automacros'''
'''Automacros'''


Automacros are macro which automatically trigger when certain conditions are met, just like how blocks in config.txt trigger depending on the conditions set in them.
Automacros are macros which automatically trigger when certain conditions are met, just like how blocks in config.txt trigger depending on the conditions set in them.


The point of automacros is that you use them to check for conditions. When the condition is fulfilled you can either respond to it in the automacro or call a macro to do it. The format for automacros is...
The point of automacros is that you use them to check for conditions. When the condition is fulfilled, you can either respond to it in the automacro or call a macro to do it. The format for automacro is...


  automacro <name> {
  automacro <name> {
Line 974: Line 1,318:
                 …..
                 …..
         }
         }
         timeout <n seconds> (if necessary)
         timeout <n seconds> #(if necessary)
  }
  }




For example, suppose you're playing manually but your slave priest is on Kore, and you want it to warn you if its running out of sp, you can use an automacro like this....
For example, suppose you're playing manually but your slave priest is on Kore, and you want it to warn you if it's running out of sp, you can use an automacro like this....


  automacro sp {
  automacro sp {
Line 991: Line 1,335:
Taking it line by line,
Taking it line by line,


''automacro sp {'' - you need to put automacro to tell kore that its an automacro. Then you put the name of the macro, in this case its “sp”. Then you need to put an opening bracket “{“ which tells Kore that the automacro's code begins there.
#'''''automacro sp {''''' - you need to put automacro to tell kore that its an automacro. Then you put the name of the macro, in this case its “sp”. Then you need to put an opening bracket “{“ which tells Kore that the automacro's code begins there.
#'''''sp < 200''''' – After the “{“, you put the conditions on successive lines. Here, there is only one condition and I think its clear that the condition is that sp should be below 200.
#'''''call {''''' – The word “call” tells Kore that now you're going to be putting commands, not conditions. The opening bracket “{“ tells Kore that the commands will start now. These commands are the ones that will get carried out when the conditions are met, here when sp goes below 200.
#'''''do c sp low''''' – “do” tells Kore that this is a command, something to be done. After “do”, just leave a space and type in the console command you want to execute exactly the way you do in the Kore console.
#'''''}''''' – This closing bracket “}” tells Kore that the commands have ended.
#'''''timeout 10''''' – This isn't a condition or a command, it works the same way it works in normal Kore blocks, so this automacro cannot trigger within 10 seconds of it already having been triggered. I ALWAYS put timeouts, so even if my conditions are faulty, the automacro doesn't spam and mess up whatever my bot is doing. Otherwise if your automacro is messed up then you could die or something, so its better to put an appropriate timeout.
#'''''}''''' – This closing bracket “}” tells Kore that your automacro code has ended.


''Sp < 200'' – After the “{“, you put the conditions on successive lines. Here there is only one condition and I think its clear that the condition is that sp should be below 200.
So basically whenever, the bot's sp goes below 200, it says in public chat “sp low”. This also has the advantage of making your bot not look like a bot =p.
 
''call {'' – The word “call” tells Kore that now you're going to be putting commands, not conditions. The opening bracket “{“ tells Kore that the commands will start now. These commands are the ones that will get carried out when the conditions are met, here when sp goes below 200.
 
''do c sp low'' – “Do” tells Kore that this is a commands, some to be done. After “do”, just leave a space and type in the console command you want to execute exactly the way you do in the Kore console.
 
''}'' – This closing bracket “}” tells Kore that the commands have ended.
 
''timeout 10'' – This isn't a condition or a command, it works the same way it works in normal Kore blocks, so this automacro cannot trigger within 10 seconds of it already having been triggered. I ALWAYS put timeouts, so even if my conditions are faulty, the automacro doesn't spam and mess up whatever my bot is doing. Otherwise if your automacro is messed up then you could die or something, so its better to put an appropriate timeout.
 
''}'' – This closing bracker “}” tells Kore that your automacro code has ended.


So basically whenever, the bot's sp goes below 200, it says in public chat “sp low”. This also has the advantage of making your bot not look like a bot =p.


Here's another macro which illustrates how you can use macros to handle weird situations which Kore is not equipped to deal with. When fighting against metalings, the often strip you and then your oh-so-smart bot punches! To get around this, put the weapon you use in default auto equip and use this macro.
Here's another macro which illustrates how you can use macros to handle weird situations which Kore is not equipped to deal with. When fighting against metalings, they often strip you and then your oh-so-smart bot punches! To get around this, put the weapon you use in default auto equip and use this macro.


  automacro strip {
  automacro strip {
Line 1,021: Line 1,360:
Taking it line by line....
Taking it line by line....


''automacro strip {'' - Tell Kore this is an automacro, and it's name is "strip". The '{' tells Kore the code of the automacro starts here.
#'''''automacro strip {''''' - Tell Kore this is an automacro, and it's name is "strip". The '{' tells Kore the code of the automacro starts here.
''
#'''''status Strip Weapon''''' - The only condition in this macro. This checks your list of statuses for the status "Strip Weapon". Thus, when you are stripped by a Metaling, you get the "Strip Weapon" status and this condition will be fulfilled.
status Strip Weapon'' - The only condition in this macro. This checks your list of statuses for the status "Strip Weapon"
#'''''call {''''' - The word "call" and "{" tells Kore that the commands to be executed start after the '{'
 
#'''''do tele''''' - "do" tells Kore that what comes after is a console command to be carried out. Here, the command is "tele" so you teleport away from the Metaling, so that in case you lag or something, you aren't killed while executing the next instruction which is.....
''call {'' - The word "call" and "{" tells Kore that the commands to be executed start after the '{'
#'''''do relog 10''''' - Tells Kore to relogin in 10 seconds, because when you login again, your "strip weapon" status is cleared.
#'''''}''''' - The closing bracket '}' tells Kore the commands have ended.
#'''''timeout 10''''' - Ensures automacro is not spammed, and 10 seconds is enough time to execute the automacro. So after 10 seconds, if for some reason you are still stripped, this automacro will trigger again. However, if you re-login and are stripped within 10 seconds, then macro will not trigger again untill the 10 seconds are up.
#'''''}''''' - The closing bracket '}' tells Kore the macro code has ended.


''do tele'' - 'do' tells Kore that what comes after is a console command to be carried out. Here the command is "tele" so you teleport away from the Metaling, so that incase you lag or something, you aren't killed while executing the next instruction which is.....
Summarising, on being stripped, you teleport away from the monster and then relogin, with a timeout of 10 seconds to ensure that the automacro is not spammed. Since the weapon you're using is filled in the default weapon option, after re-logging in, Kore will automatically re-equip that weapon.
''
do relog 10'' - Tells Kore to now relog, so that when you login again, your "strip weapon" status will be cleared.


''}'' - '}' Tells Kore the commands have ended.


''timeout 10'' - Ensures automacro is not spammed, and 10 seconds is enough time to execute the automacro. So after 10 seconds, if for some reason you are still stripped, this automacro will trigger again. However, if you relogin and are stripped within 10 seconds, then macro will not trigger till the 10 seconds are up.
Let's try a more complicated macro. If you have a priest, you would want it to warp back to its lockmap or the nearest warp-able map near its lockmap. This macro does it. From the savemap, the bot takes the same route to the lockmap each time. So in the first map outside the town, just set it to move to a designated tile, and cast warp on a nearby tile, and then move to that tile. You will need delays to allow the spell to be cast and commands to be completed.
 
''}'' - '}' tells Kore the macro code has ended.
 
Summarising, on being stripped, you teleport away from the monster and then relogin, with a timeout of 10 seconds to ensure that the automacro is not spammed. Since the weapon you're using is filled in in the default weapon option, after relogging Kore will automatically re-equip that weapon.
 
Lets try a more complicated macro. If you have a priest, you would want it to warp back to its lockmap or the nearest warp-able map near its lockmap. This macro does it. From the savemap, the bot takes the same route to the lockmap. So the first map outside the town, just set it to move to a designated tile, and cast warp on a nearby tile, and then more to the tile. You will need delays to allow the spell to be cast and commands to be completed.


  automacro warp {
  automacro warp {
Line 1,061: Line 1,394:
Taking it line by line,
Taking it line by line,


''automacro warp {'' - As explained, the automacro's name is warp, and “{“ indicates that the automacro begins.
#'''''automacro warp {''''' - As explained, the automacro's name is warp, and “{“ indicates that the automacro begins.
#'''''map get_fild07''''' - The first condition, the automacro will only trigger if the map is gef_fild07.
#'''''inventory “Blue Gemstone” > 0''''' - Second condition, ensures there is atleast one blue gemstone in the inventory so that warp portal can be cast. Obviously you need a getAuto in config.txt for the gemstones.
#'''''call {''''' - Tells Kore that the commands start here.
#'''''do ai manual''''' - Frequently, I use this command in the more complicated and longer macros, so that Kore doesn't get diverted into doing something else while your macro is running, for example, attacking a monster or gathering up an item. In this case I'm using it so that the bot doesn't continue walking.
#'''''pause 1''''' - Inserts a delay of 1 second before the next command is carried out, VERY IMPORTANT. If you don't use delays then Kore can and will miss steps in the macro and the result is a mess.
#'''''do move 319 187''''' - The “move” console command is used to move your bot to a set location.
#'''''do sl 27 316 188''''' - The “sl” console command is used to make your priest cast warp portal on a convenient spot near your location, in this case (316,188).
#'''''pause 2''''' - Very Important. A delay of 2 seconds is put, allowing enough time to cast warp portal. If you priest has low dex, this delay should be increased.
#'''''do warp 1''''' - Console command “warp” is used to select memo location 1.
#'''''pause 1''''' - 1 second delay to allow the portal to form.
#'''''do move 316 188''''' - Move to the portal and Voila! You have been warped to the map.
#'''''do ai on''''' - You set ai to manual at the beginning of this macro, so now you need to turn it back on.
#'''''}''''' - Closing bracket indicates end of commands.
#'''''timeout 20''''' - Ensures that the bot does not spam the automacro. The timeout should be large enough to allow the completion of the automacro, so that the automacro does not trigger again while it is in execution.
#'''''}''''' - Closing bracket indicates end of automacro code.


''map get_fild07'' - The first condition, the automacro will only trigger if the map is gef_fild07.
''Note the use of appropriate delays and timeouts in this automacro.''


''Inventory “Blue Gemstone” > 0'' - Second condition, ensures there is atleast one blue gemstone in the inventory so that warp portal can be cast. Obviously you need a getAuto in config.txt for the gemstones.


''Call {'' - Tells Kore, that the commands start here.
The macro plugin also has many useful built-in variables which give you your position for example. It's all listed in the macro manual. I just want to show how to extract your position from these variables.


''do ai manual'' - Frequently, I use this command in the more complicated and longer macros, so that Kore doesn't get diverted into doing something else while your macro is running, for example, attacking a monster or gathering up an item.
'''$.pos''' gives you your position. Now to get your ''x'' and ''y'' coordinates the code is,


''Pause 1'' - Inserts a delay of 1 second before the next command is carried out, VERY IMPORTANT. If you don't use delays then Kore can and will miss steps in the macro and the result is a mess.
  $px = @arg ("$.pos", 1)
  $py = @arg ("$.pos", 2)
 
Here, the ''x'' coordinate is the 1st value in '''$.pos''' so we have a ''''1''''. Similarly, the ''y'' coordinate is the second value in '''$.pos''' so we have a ''''2''''.


''do move 319 187'' - The “move” console command is used to move your bot to a set location.
If you have a “'''monster'''” condition in your automacro, '''$.lastmonsterpos''' gives the position of that monster. To extract the ''x'' and ''y'' coordinates the code is,


''do sl 27 316 188'' - The “sl” console command is used to make your priest cast warp portal on a convenient spot near your location, in this case (316,188).
  $mx = @arg ("$.lastMonsterPos", 1)
  $my = @arg ("$.lastMonsterPos", 2)


''pause 2'' - Very Important. A delay of 2 seconds is put, allowing enough time to cast warp portal. If you priest has low dex, this delay should be increased.


''Do warp 1'' - Console command “warp” is used to select memo location 1.


''pause 1'' - 1 second delay to allow the portal to form.
"'''run-once'''"


''do move 316 188'' - Move to the portal and Voila! You have been warped to the map.
Frequently, the run-once condition is used instead of a timeout. It allows the automacro to run only once so that the automacro isn't spammed. To get the automacro to run again, a release command needs to be executed. However, due to a rogue situation that the macro hangs and the release macro isn't executed, your automacro is effectively stuck. To get around this, use a timeout instead of using run-once.


''Do ai on'' - You set ai to manual at the beginning of this macro, so now you need to turn it on.


''}'' - Closing bracket indication end of commands.
'''The normal macros'''


''timeout 20'' - Ensures that the bot does not spam the automacro. The timeout should be large enough to allow the completion of the automacro, so that the automacro does not trigger while the macro is executed.
In the 3 examples given, everything was done using automacros. So you're probably wondering what the point of macros is if everything can be done using automacros. Well, not everything can be done using automacros. For example, it is not possible to use macro variables such as $.pos in an automacro. So, if you get errors like “''not an automacro variable''”, it means that it cannot be done in an automacro but has to be done using a macro. In such a case, the automacro needs to call a macro.


''}'' - Closing bracket indicates end of automacro code.


''Note the use of appropriate delays and timeouts in this automacro.''
'''Regular Expressions'''
 
Sometimes, in a macro, you need to compare, say the names of characters near you to see if it matches a certain name. This is where regular expressions come in handy. If you're new to macros, you can read this later as it can be a bit complicated, the more advanced regexps can look hellish =P Visit http://www.regular-expressions.info/quickstart.html where it's explained fairly well. I'm just going to put down some basics here.
 
$.lastpubMsg = /(A|a)uto (S|s)torage/
 
Here, '''$.lastpubMsg''' is your macro variable holding the last public chat message. A regular expression can just be plain text so it could have been /auto storage/ or /auto/ and Kore would have checked if the public chat contained "auto storage" or "auto".


However, here we have used the "'''|'''" symbol which means "'''or'''". Notice ''''A'''' and ''''a'''' are contained in brackets with a ''''|'''' between them. It means both ''''auto'''' and ''''Auto'''' are checked for. Similarly, both ''''storage'''' and ''''Storage'''' are checked for.


The macro plugin also has many useful built in variables which give you your position for example. Its all listed in the macro manual. I just want to show how to extract your position from these variables.


$.pos gives you your position. Now to get your x and y coordinates the code is,
Now suppose you want to check for repetition, say 'aauto storage' also, then you may use:


  $px = @arg ("$.pos", 1)
/(a+|A+)uto (S|s)torage/
  $py = @arg ("$.pos", 2)


If you have a “monster” condition in your automacro, $.lastmonsterpos gives the position of that monster. To extract the x and y coordinates the code is,
Notice I've used a ''''+''''. This tells Kore that the character it is directly after, in this instance ''''a'''' or ''''A'''', should be present one or more times. So even if someone says "aaaaaaaaaauto storage', it will be valid. Other checks like the ''''+'''' are:
:* '''*''' matches 0 or more times '''e.g.''' /(a*|A*)uto Storage/ so "uto storage" will also work.
:* '''+''' matches 1 or more times '''e.g.''' as above.
:*'''?''' matches 1 or 0 times '''e.g.''' /(a?|A?)uto Storage/ so "uto storage" and "auto storage" work but "aaaauto storage" won't.


  $mx = @arg ("$.lastMonsterPos", 1)
  $my = @arg ("$.lastMonsterPos", 2)


So if you want to check for "Kobold" just do /Kobold/ and if "Kobold" is anywhere in the string, it will be valid. For Kobold-1 and Kobold-2 only it will be /Kobold-(1|2)/.


Regexps may also come in handy when you're using the "console /<regexp>/" condition in automacros, if you need to check for a variety of trigger texts.


'''Run Once'''
These are just the basics. I recommend referring to the link provided above if you need to make a more complicated regexp.
Frequently, the run once condition is used instead of a timeout. It allows the automacro to run only once, so that automacro isn't spammed. To get the automacro to be run again, a release command needs to be executed. However, sometimes due to a rogue situation the macro can hang and the release macro isn't executed. This means your automacro is effectively stuck. To get around this I use a timeout instead of using run-once.


== How to write a macro ==
Now that you have some idea of what a macro is, try writing one


'''The normal macros'''
* Figure out under exactly what conditions you want your automacro to trigger
In the 2 examples above, I've done everything using automacros. So you're probably wondering what the point of macros is, if everything can be done using automacros. Well, not everything can be done using automacros. For example, I don't think you can use macro variables such as $.pos in an automacro. So, if you get errors like “not an automacro variable” it means that cannot be done in an automacro and has to be done in a macro.
*Logically think out the steps your bot needs to perform in the automacro. Go step by step over this carefully.
*Ensure you have inserted appropriate pauses between the steps.
*Ensure you have a timeout or a run-once in your macro to prevent spamming of it.
*Now put the code in macros.txt and start the bot or reload macros.txt. If all is fine you won't get any error messages. But if there is a problem in the syntax, say you missed out a “}”, then you will get an error message and the macro won't work. Figure out what the error is, correct it, and reload macros.txt again to check if you corrected it properly or not.


'''Regular Expressions'''
== Collected Macros ==
Sometimes, in a macro you need to compare say the names of characters near you to see if it matches a certain name. This is where regular expressions come in handy. If you're new to macros, you can read this later as it can be a bit complicated, the more advanced regexps can look hellish =P Visit http://www.regular-expressions.info/quickstart.html where its explained fairly well. I'm just going to put down some basics here.


$.lastpubMsg = /(A|a)uto (S|s)torage/
I would like to share some useful macros, hopefully not angering anyone here.


Here $.lastpubMsg is your macro variable holding the last public chat message. A regular expression can just be plain text so it could have been /auto storage/ or /auto/ and Kore would have checked if the public chat contained "auto storage" or "auto".
'''Identify with Magnifier'''


However here we have used the "|" symbol which means "or". Notice 'A' and 'a' are contained in brackets with a '|' between them. It means both 'auto' and 'Auto' are checked for. Similarly both 'storage' and 'storage' are checked for. Now suppose you want to check for repetition, say 'aauto storage' also, then you can use /(a+|A+)uto (S|s)torage/. Notice I've used a '+'. This tells Kore that the character it is directly after, here 'a' or 'A', should be present one or more times. So even if someone says "aaaaaaaaaauto storage' it will be valid. Other checks like the '+' are
You got unidentified Items in your inventory and want to identify them using magnifiers? Use "macro id" on console until all items are identified. You don't have to type anything else, the macro will identify the first item found, after that the second and so on.
* Match 0 or more times eg. /(a*|A*)uto Storage/ so "ato storage" will also work
+ Match 1 or more times eg. as above
? Match 1 or 0 times eg /(a?|A?)uto Storage/ so "ato storage" and "auto storage" work but "aaaauto storage" won't


So if you want to check for "Kobold" just do /Kobold/ and if "Kobold" is anywhere in the string, it will be valid. For Kobold-1 and Kobold-2 only it will be /Kobold-(1|2)/.
macro id {
        $id = @inventory(Magnifier)
        do is $id
        pause 1
        do identify 0
}


Regexps can also come in handy when you're using the "console /<regexp>/" condition in automacros, if you need to check for a variety of trigger texts.
'''Automatically refine Rough Stones (Ori / Elu)'''


These is just the basics, I recommend referring to link provided above if you need to make a more complicated regexp.
This macro, once called, will automatically walk to the prontera forge and refine Rough Ori / Elu as long as there are more than 5 in your inventory. I did not find something like that yet, please post everything (better) I may have missed.


== How to write a macro ==
Just fire ''macro ref'' to start. To stop manually, type ''macro stop''.
Now that you have some idea of what a macro is, try writing one


  -Figure out under exactly what conditions you want your automacro to trigger
  macro ref {
  -Logically think out the steps your bot needs to perform in the automacro. Go step by step over this carefully.
        do move prt_in 59 60
-Ensure you have inserted appropriate pauses between the steps.
        call ref-while
-Ensure you have a timeout or a run-once in your macro to prevent spamming of it.
}
  -Now put the code in macros.txt and start the bot or reload macros.txt. If all is fine you won't get any error messages. But if there is a problem in the syntax, say you missed out a “}”, then you will get an error message and the macro won't work. Figure out what the error is, correct it, and reload macros.txt again to check if you corrected it properly or not.
  macro ref-while {
        log start refining with
        $ori = @invamount (Rough Oridecon) R. Oris
        $elu =  @invamount (Rough Elunium) R. Elus
        log $ori $elu
        while (@invamount (Rough Oridecon) > 4) as loop
                do talk 0
                pause 0.8
                do talk resp 0
                call ref-while
        end loop
        while (inventory (Rough Elunium) > 4) as loop
                do talk 0
                pause 0.8
                do talk resp 1
                call ref-while
        end loop
        stop
  }


== FAQ ==
== FAQ ==
; I have an automacro that checks for the amount of an item in my inventory / cart to be less than a given value or equal to zero (e.g. inventory "red potion" <= 30) but that automacro triggers also on map change. Why is that so and what should I do?  
; I have an automacro that checks for the amount of an item in my inventory / cart to be less than a given value or equal to zero (''e.g. inventory "red potion" <= 30'') but that automacro triggers also on map change. Why is that so and what should I do?  
: When you're changing the map all items vanish from your inventory for a short time. That happens with the official client, too. To avoid this, add an additional check for an item that you always carry with you, like inventory "Jellopy" > 0
: When you're changing the map all items vanish from your inventory for a short time. That happens with the official client, too. To avoid this, add an additional check for an item that you always carry with you, like inventory "Jellopy" > 0


Line 1,153: Line 1,530:




; I'm getting Malformed UTF-8 character (fatal), what is this?
; I'm getting Malformed UTF-8 character (''fatal''), what is this?
: This error happens when you used some special character on some of your macros. To solve it, open your macros.txt and if you're using Notepad, when you are going to save, change Encoding to '''UTF-8'''.
: This error happens when your macros.txt is not saved in UTF-8 encoding.
If you're using Notepad++, go to '''Format > UTF-8 without BOM''' and save.
: To solve it, open your macros.txt and if you're using Notepad, when you are going to save, change Encoding to '''UTF-8'''. '''(If that does not help - do not use Notepad.)'''
: If you're using Notepad++ or other text editor, go to '''Format > UTF-8 (without BOM)''' and save.
 


; I dont understand english. Where is ru-version?
: Вот ссылка на [http://ragbot.ru/wiki/Макро-плагин перевод данного мануала] на русский.


== Author ==
== Author ==
The macro plugin was written by '''arachno'''.
The first version of the plugin was written by macro '''arachno'''.
Special thanks to '''ezza''' who is updating the macro plugin now.
After that, various contributors, such as ezza, daigaku, keplerbr, eternalhavest (creator of the macro debugger to [[Wx Interface]]) and technologyguild helped in the development of macro plugin to be what it is today.
 
== Related tools and resources ==
 
* [http://forums.openkore.com/viewtopic.php?f=32&t=8927  syntax highlight and context help for Notepad++ (en)]
* [http://ragbot.ru/forum/showthread.php?t=5274 syntax highlight and context help for Notepad++ (ru)]
[[Category:Plugins]]

Latest revision as of 22:49, 11 August 2021

With this plugin you can predefine command sequences (macros) which are run either manually or by situation-dependent triggers. The latest version is 2.0.3.

Installation

or checkout it from SVN:

svn co svn://svn.code.sf.net/p/openkore/code/plugins/macro/trunk/ macro
  • Go to your OpenKore main folder (the folder which contains the file openkore.pl) and create a subfolder called plugins, if there isn't already one. Create a subfolder macro in it.
  • Inside the Macro plugin's zipfile, you will find the file macro.pl, the folder Macro and other files and folders. Extract them to your plugins/macro folder, so macro.pl ends up in plugins/macro/macro.pl.
  • In your OpenKore control folder, create a blank file named macros.txt. In this file you will put your macros/automacros.


After installation, your OpenKore file tree should look like this (ignoring OpenKore's own files):

openkore
|-- openkore.pl
|-- control
|   |-- macros.txt
|-- fields
|-- logs
|-- plugins
|   |-- macro
|   |   |-- Macro
|   |   |   |-- Automacro.pm
|   |   |   |-- Data.pm
|   |   |   |-- Parser.pm
|   |   |   |-- Script.pm
|   |   |   |-- Utilities.pm
|   |   |-- macro.pl
|-- src
|-- tables

Console Commands

Syntax

macro <macroname> [options] [-- parameter(s)]


Runs macro <macroname>.

Option Value Description
-repeat n repeat the macro n times
-overrideAI none override openkore's AI
-macro_delay delay override global macro_delay for this macro
-exclusive none do not allow automacros to cancel this macro
-orphan method use method for handling orphaned macros


Parameters for the macro can be specified after a double dash (--). These parameters are saved to the variables $.param1 to $.paramN. Example:

macro foo {
 log Parameter 1 is $.param1
 log Parameter 2 is $.param2
}


When called as macro foo -- foo bar it would print out

[macro] Parameter 1 is foo
[macro] Parameter 2 is bar


macro list
Lists all available macros.
macro stop
Stops current macro.
macro pause
Interrupts the running macro.
macro resume
Resumes an interrupted macro.
macro version
Prints version number.
macro reset [<name(s)>]
Resets all run-once automacros or the specified automacro <name>.
macro status
Shows whether or not a macro is currently running. If that's the case it shows the delay for the next command, the current line, overrideAI setting, whether or not it has finished and whether or not the macro registered to AI queue.
macro varstack
Show all variables used by the macro plugin (special or custom).

Configuration files

control/macros.txt
Put your macros and automacros in here. You can change the file's name depending on what you configured on config macro_file.
control/timeouts.txt
Add macro_delay and set it to the number of seconds you want the plugin to pause between commands.
control/config.txt
Option Value Default Description
macro_nowarn boolean 0 enable or disable the annoying warnings when not using call in your automacro(s)
macro_orphans terminate
reregister
reregister_safe
terminate override openkore's AI
macro_file file name macros.txt file containing the macros
macro_allowDebug boolean 0 console-check also processes openkore's debug messages. Warning: slows down the plugin.

Macro Syntax

macro MacroName {
   do this..
   and that..
   yattayatta..
}
  • You can use any name you want for your macro. Be careful not to make two macros with the same name.
  • Macros is executed from top to bottom.
  • Only macro instructions can be used in macros. If you need to use console command, use do.

Macro Instructions

do <command>
Run <command>, as if it was entered in OpenKore terminal. Commands are from Console Commands.
macro foo {
   do move 123 234 prontera
   do sit
   do c hello world
}

The command ai clear is disabled by default in the plugin.
If has a macro the command do ai manual or do ai off , the macro will stop its execution.


log <text>
Prints a text in the console. Can contain macro $variables and @stuff.
macro foo {
  log This line logs a text to console.
  log All your base are belong to us!
}


pause [<n>]
Pauses the macro for n seconds.
macro foo {
   log It's 10:00:00
   pause 10
   log Now it's 10:00:10 
   log 10 seconds have passed after the first print.
}

pause not only pauses the macro running, pauses all our character's actions.


call <macroname> [<n>]
Calls macro <macroname> [<n> times]. When <macroname> is finished the current macro continues.


release (<name> | all)
Reenables a locked automacro ("run-once" keyword or locked by "lock") or reenables all automacros when using release all.


lock (<name> | all)
Locks an automacro and disables it's checks. To lock all automacros, use lock all.


stop
Immediately terminates the running macro.


set <option> <value>
Sets macro features:
  • orphan method
  • macro_delay timeout
  • overrideAI [0|1]
  • repeat times
  • exclusive [0|1]

Variable declaration and usage

You can work with variables. Variable declaration is not needed. All macro variables are global.

Set a value of variable:

$variable = value

Get a value of variable (note that this is "Macro Syntax" section, so it doesn't apply to automacro conditions, config options etc):

$variable
macro Hello {
   $var = Hello
   $var1 = World!
   log $var $var1
}

This would print in the console:

[macro] Hello World!


If you want to use the $ symbol you should escape it with \.

macro money {
   log I have a lot of \$
}


It is possible to increment and to decrement a variable using $variable++ or $variable--.

macro Counter {
    $counter = 0
    log Counter is at $counter
    $counter++
    log Now it's $counter
    $counter--
    log It's back to $counter
}

The result is:

[macro] Counter is at 0
[macro] Now it's 1
[macro] It's back to 0

You can also unset/vanish the existing variable using 'undef' or 'unset':

$x = 1
log \$x is $x
$x = undef  # or you can use 'unset'
log \$x now vanished: $x

Variable assignment doesn't evaluate, it only does macro substitutions, so use @eval for calculations.

macro math {
    $num = 2
    $num2 = 3
    $result = @eval($num+$num2)
    log sum of $num and $num2 is $result
}

The result is:

[macro] sum of 2 and 3 is 5

You can extract the first element from a comma-separated list using like this:

macro foo {
  $list = banana, apple, strawberry, grape
  $var = [$list]
  log The first element of \$list is $var
  log Now \$list contains $list
}

The result is:

[macro] The first element from \$list is banana
[macro] Now $list contains apple, strawberry, grape

Note: Variable names may only contain letters and numbers.

Nested Variables

You can define dynamic or nested variables.

macro foo {
    $name = Camila
    ${$name} = Brazil   # Note: ${name} is equal to $Camila now
    log $name lives on ${$name}
}

The result is:

[macro] Camila lives on Brazil

Special Variables

There are special read-only variables which begin with a dot. They are pre-defined with the macro plugin.

  • $.map - the map you're on ("prontera")
  • $.pos - your current position ("123 234")
  • $.time - current time as unix timestamp ("1131116304")
  • $.datetime - current date and time ("Fri Nov 4 15:59:36 2005")
  • $.hour - current hour time in 24h format
  • $.minute - current minute time
  • $.second - current second time
  • $.hp - current hp
  • $.sp - current sp
  • $.lvl - current base level
  • $.joblvl - current job level
  • $.spirits - current number of spirit spheres
  • $.zeny - current amount of zeny
  • $.status - current statuses in a comma-separated list
  • $.paramN - command line parameters (see Syntax)
  • $.caller - name of the last triggered automacro
  • $.weight - returns the current weight of the character
  • $.maxweight - returns the maximum weight of the character

Special Keywords

These keywords (having common form of @<keyword>(<arguments>) each) interpolated to corresponding values just about anywhere inside macro blocks (except goto, end, $var=[$list], LHS of ${$var} assignment, label definitions, macro name in call, set). Macro variables can be used in arguments, with the exception for @nick().

@npc (<x> <y> | /regexp/i | "<name>")
Return NPC's index which location is <x> <y> or NPC's name match regexp or NPC's name is equal to <name> . Returns -1 if no NPC was found.
@inventory (<item>)
Returns inventory item index of <item>. If <item> doesn't exist, it returns -1.
@Inventory (<item>)
Same as @inventory but returns all matching indexes as a comma-separated list or -1 if the item was not found.
@invamount (<item>)
Returns the amount of the given <item> in inventory.
@cart (<item>)
Returns cart item index of <item>. If <item> doesn't exist, it returns -1.
@Cart (<item>)
Same as @cart but returns all matching indexes as a comma-separated list or -1 if the item was not found.
@cartamount (<item>)
Returns the amount of the given <item> in cart.
@storage (<item>)
Returns storage item index of <item>. If <item> doesn't exist, it returns -1.
@Storage (<item>)
Same as @storage but returns all matching indexes as a comma-separated list or -1 if the item was not found.
@storamount (<item>)
Returns the amount of the given <item> in storage.
@player (<name>)
Returns player index of player <name>. If player <name> is not found, it returns -1.
@monster (<name|ID>)
Returns monster index of monster <name|ID>. If monster <name|ID> is not found, it returns -1.
@vender (<name>)
Returns vender index of vender <name>. If vender <name> is not found, it returns -1.
@store (<name>)
Looks for an item in a store and returns index or -1 if the item was not found.
@shopamount (<item>)
Returns the amount of the given <item> in shop.
@random ("<argument1>", "<argument2>", ...)
Returns randomly one of the given arguments .
@rand (<n>, <m>)
Returns a random number between (and including) <n> and <m>.
@eval (<argument>)
Evaluates the given <argument>. Contents of @eval is Perl and does NOT have macro plugin syntax, except for variables and @() substitution.
@arg ("<argument>", <n>)
Returns the <n>th word of <argument> or an empty string if the word index is out of range.
@config (<variable>)
Returns the value of <variable> specified in config.txt.
@venderitem (<name>)
Looks for an item in a player's shop and returns index or -1 if the item was not found.
@venderprice (<indexID>)
Looks for an item in a player's shop and returns its price.
@nick (<word>)
Escapes all the regexp metacharacters and some of the perl special characters with \ (a backslash). Especially for player's name.

Chaining commands

You can run multiple commands one after another without having to wait for openkore's AI or macro_delay or whatever. Just enclose these commands with [ and ].

0 macro foo {
1  do whatever
2  log yet another line
3  [
4     do something
5     do something else
6     log foo
7  ]
8  log done
9 }

Line 3 starts the chaining mode. This line has no delay. Lines 4, 5 and 6 are run as soon as the previous command has finished with no delay and they cannot be interrupted. Line 7 stops the chaining mode and line 8 will be run $macro_delay seconds after that.

Sub-lines

Instead of using one command/var assignments per line, you can separate them using a semi-colon ";". There were few syntaxes that you could use it to minimize the lines with this function such as Var Assignment ($var), Double/Nested Var (${$var}), Increase(++) or Decrease(--) Var or Double/Nested Var, Set, Lock, Release, Log (Use separate line if you want to use ";" in the log message), Pause, Do and Perl Sub-Routine Macro Command. Further info: Forums

macro foo {
    $i = 1; pause 5; log \$i = $i; $ii = 2; $iii = 3; $i++; $ii--; lock automacroName; release automacroName; set overrideAI 1
}

Commands separated by a semi-colon ";" will have no delay between them, just like the Chaining commands, unless on certain commands such as pause and log.

Conditions

Supported conditions in macro plugin are:

Operator Description
< less than
<= less or equal to
== or = equal to (link)
> greater than
>= greater or equal to
!= not equal to
~ <left part> is element of <right part (comma-separated list)>
=~ <left part> matches regular expression <right part>

Further Discussion: Forums

arg .. arg2 Between arg and arg2. Where arg can be a number, a % or a variable.

Flow control and labels

Just as prevalent in high-level languages ​​constructs like "if .. else", "while", "foreach", "for .. next", "do .. while" and other commands, the macro plugin also has some "if", "else", "elsif", "switch", "case, "goto" and "while".. Since there are no (visible) line numbers, you'll need to use labels which can be defined by a colon followed by the name of the label.

Syntax of command conditions

If

Right now, macro if conditions are very close to the perl if statements. It accepts unlimited number of statements in just one if condition line, regexp matching is allowed (unfortunately, no backreference using parenthesis) and the use of && for and meaning and || for or meaning.

You can use a simple statement;

if (arg1 <Conditions> arg2) (goto <label> | call <macro> [<n>] | stop | { )

A simple statement with OR condition;

if (arg1 <Conditions> arg2 || arg3 <Conditions> arg4) (goto <label> | call <macro> <n> | stop | { )

A simple statement with AND condition;

if (arg1 <Conditions> arg2 && arg3 <Conditions> arg4) (goto <label> | call <macro> <n> | stop | { )

Or with both;

if ((arg1 <Conditions> arg2 || arg3 <Conditions> arg4) && arg5 <Conditions> arg6) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <Conditions> arg2 && arg3 <Conditions> arg4) || arg5 <Conditions> arg6) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <Conditions> arg2 && arg3 <Conditions> arg4) || (arg5 <Conditions> arg6 && arg7 <Conditions> arg8)) (goto <label> | call <macro> <n> | stop | { )
if ((arg1 <Conditions> arg2 || arg3 <Conditions> arg4) && (arg5 <condition> arg6 || arg7 <condition> arg8)) (goto <label> | call <macro> <n> | stop | { )
  • Notice inside of each brackets containing the AND and OR symbols.

Where;

  • arg can be a variable, a nested variable, a special keyword, @eval, letters and numbers or even a Perl Subroutines function.
  • <label>, the name of an existing label, can only contain letters and numbers
  • <macro>, the name of a existing macro, and
  • <n>, the number of times it will call the macro.
  • < { >, the beginning of a block of commands that will be executed if the condition is true, to finish it, use }.


Note:

  • If <n> is defined as 0 or even if it is undefined, the called macro name will run at once and then stop, not continuing the previous macro caller at a run time.
  • If <n> is greater than 0, the called macro name will run at n time/s and then continuing the previous macro caller line at a run time.


Note²: If statements are unlimited, you can use as many statements as you want.

if (arg1 <Conditions> arg2 || arg3 <Conditions> arg4 || ... || argN <Conditions> argN+1) (goto <label> | call <macro> <n> | stop)

Where;

  • arg can be a variable, a nested variable, a special keyword, @eval or letters and numbers.
  • All the conditions is up to the Nth argument <Conditions> N+1th argument.
  • While N is an integer number/s which is greater than zero.
Postfix control

Another way to use the if is to put it at the end of the command.

<command> if (arg1 <Conditions> arg2)

The command before the if statement will only be executed if the condition is true. The way to create the condition is the same as previously explained.

In this example below, the two commands are equivalent:

call buy if ($.zeny > 1000)
if ($.zeny > 1000) call buy

Else

In case if is used with open braces ( { ), it's possible to use the command else along closed braces ( } ) in the end of the command block if, to start a new command block that will be executed if if is false.

if (arg1 <Conditions> arg2) {
   command1
   command2
   ...
   commandN
} else {
   command1
   command2
   ...
   commandN
}

In this case, the first command block will be executed if the condition turns out being true. If it's false, the second command block (preceding by else) will be executed.

Note: Inside braces you're allowed to use tons of commands.
Note: It is not compulsory to wear else.

Elsif

It is like the else + if. The elsif is in the same place else with the difference being added conditions to its command block is executed if its conditions are true.

if (arg1 <Conditions> arg2) {
   command1
   command2
   ...
   comandoN
} elsif (arg1 <Conditions> arg2) {
   command1
   command2
   ...
   commandN
} else {
   command1
   command2
   ...
   commandN
}

In this case, the first block of commands will be executed if the condition is true. If it is false, the second block of commands (preceded by the elsif) will be executed if its condition is true. Should also be false, the command blocks else to be executed.

Note: As in the else, inside braces of elsif you're allowed to use tons of commands.
Note: It is not compulsory to wear the else or elsif.

Switch/case

It's similar to if followed by enumerous elsif. It's useful to leave the code cleaner when the paramater being analyzed is the same, this way you don't need to repeat it.

switch (arg1) {
    case (<Conditions> arg2) (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
    case (<Conditions> arg2) (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
    ...
    else (goto <label> | call <macro> <n> | stop | {)
        (If you use "{", the commands should be used here and in a separated line that should be closed with the block "}")
}

Note: The use of else is optional. Note: In case that no case ends being true, else will be activate if it exists.

Examples

This macro will go a random walk

macro walk {
   $num = @rand(1, 4)
   if ($num == 1) {
       do c I will follow the path 1
       do north
   }
   if ($num == 2) {
       do c I will follow the path 2
       do south
   }
   if ($num == 3) {
       do c I will follow the path 3
       do east
   }
   if ($num == 4) {
       do c I will follow the path 4
       do west
   }
}

Simplified version of the above macro, using postfix control.

macro walk {
   $num = @rand(1, 4)
   do c I will follow the path $num
   do north if ($num == 1)
   do south if ($num == 2)
   do east  if ($num == 3)
   do west  if ($num == 4)
}

The following macro will tell if you've over 1.000z or 1.000z or less.

macro checkZeny {
   if ($.zeny > 1000) {
      do c I've over 1.000z! 
   } else {
      do c I've 1.000z or less...
   }
}

A little more complete than the above macro. Informa has more than 1.000z, has exactly 1.000z or has less than 1.000z with emoticons

macro checkZeny {
   if ($.zeny > 1000) {
      do c I've over 1.000z! 
      do e money
   } elsif ($.zeny == 1000) {
      do c I have exactly 1.000z.
      do e !
   } else {
      do c I've 1.000z or less...
      do e panic
   }
}

Similar to the above but with a different syntax

macro checkZeny {
    switch ($.zeny) {
        case (> 1000) {
            do c I've over 1.000z! 
            do e money
        }
        case (== 1000) {
            do c I have exactly 1.000z.
            do e !
        }
        else {
            do c I've 1.000z or less...
            do e panic
        }
    }
}

The below macro will print \$num is 1 if $num == 1, \$num is 2 if $num == 2, \$num is 3 if $num == 3.

macro checknum {
    $num = @rand(1, 3)
    if ($num == 1) goto one
    if ($num == 2) goto two
    if ($num == 3) goto three
    :one
    log \$num is 1
    stop
    :two
    log \$num is 2
    stop
    :three
    log \$num is 3
    stop
}

The above macro can be written using call instead of goto.

macro checknum {
    $num = @rand(1, 3)
    if ($num == 1) call one
    if ($num == 2) call two
    if ($num == 3) call three
}
macro one {
    log $num is 1
}
macro two {
    log $num is 2
}
macro three {
    log $num is 3
}


A more complicated macro

macro if {
 $i = 1
 log \$i = $i
 if (((($i = 1 || $i < 5 || $i ~ 0 .. 5) && @eval(@eval($i - 1) - @eval($i - 0)) = -1) && ($i != 2 && $i > 0 && @eval($i - 1) = 0) && ($i != 2 && $i > 0 && @eval($i - 1) = 0)) && $i = 1) goto somewhere
 if (($i = 1 || $i < 5 || $i ~ 0 .. 5) && ($i != "" && $i > 0 && @eval($i - 1) = 0)) goto somewhere
 if (@eval (@eval($i-1) - 1) != "") goto somewhere
 if ((($i = 1) || ($i < 5 && $i ~ 0 .. 5)) && ($i != "" && $i > 0 && @eval($i - 1) > 0)) goto somewhere
 log ko
 stop
:somewhere
 log OK
}

WHILE Loop

A while in macros means that certain commands will be ran while the defined conditions are met.

Syntax

while (arg <condition> arg) as <loop>
    do bla bla
    ...
end <loop>

Where;

  • arg can be a variable, a nested variable, a special keyword, @eval or letters and numbers. And
  • <loop> is the name of the loop. You can give any name you want.

Conditions can be found on Conditions.

Examples

macro while {
    $i = 0
    while ($i < 10) as loop
    log \$i = $i
    $i++
    end loop
}

The result is

[macro] \$i = 0
[macro] \$i = 1
[macro] \$i = 2
[macro] \$i = 3
[macro] \$i = 4
[macro] \$i = 5
[macro] \$i = 6
[macro] \$i = 7
[macro] \$i = 8
[macro] \$i = 9

Automacros

At this point, you can define macros and call them, typing 'macro <macro name>' in OpenKore's console input.

Automacro is an automatic trigger for calling your macros, just like blocks in config.txt are automatic triggers for certain AI actions.

Automacro block consists of:

  • one and only one automacro call option (this is different from macro call instruction) or automacro call block
  • any number of automacro conditions, which will limit automatic triggering to situation where all of them are true

Automacro does not trigger if there is currently running macro in exclusive mode. Otherwise, automacro clears macro queue (which means that all currently running macros are stopped) before call.

The point of automacro is that you use them to check for certain conditions and call your macro when all conditions are fulfilled.

Syntax

With call option, macro name must be provided:

automacro <automacro name> {
	<automacro conditions (and only them)>
	call myMacro
}
macro myMacro {
	<macro instructions (and only them, as this is regular macro)>
	# for example:
	do move prontera
	do move payon
}

With call block, you can omit standalone macro definition, and put macro instructions inside that block (it still defines a normal macro, just semi-anonymous):

automacro <automacro name> {
	<automacro conditions (and only them)>
	call {
		<macro instructions (and only them, as this is regular macro)>
		# for example:
		do move prontera
		do move payon
	}
}

Two examples above do the same thing.


The first syntax is useful if you want more than one automacro to call one simple macro.

automacro First {
	<conditions> 
	call print
}
automacro Second {
	<conditions> 
	call print
}
macro print {
	log $.caller triggered
}

Conditions

Event-based conditions only have a chance to be true (to trigger) once per corresponding event fire. Maximum of one of these conditions is allowed (otherwise, only one will be used anyway). If you use one of these conditions, you can think of it as a main trigger condition for your automacro.

State-based conditions are true (and able to trigger) as long as corresponding data meets the condition. Usually, any number of these conditions is allowed.

Developer notes for state-based conditions: multiple instances of these conditions SHOULD only be true when all of them are true (that is, as in "AND" operator). When comma-separated list of values is used, condition SHOULD be true when ANY of values apply.

Consequences:

  • if automacro with console-based condition can't trigger when corresponding event fired for some reason (other conditions are not met OR blocked by exclusive macro), it will forget about event this time
  • automacro with only hp-based conditions (or without any conditions) which calls macro without exclusive mode AND without run-once will probably enter endless loop of triggering (until environment changes by other means than macro plugin)

Some conditions set some special variables which usually contain useful information, you can use them in your macros.

Events

hook <hookname>
Triggers when openkore calls <hookname>.
save <hash key> (use in combination with hook)
Saves the value of <hash key> in a variable $.hooksaveN (N starts from 0). Currently, hooks work in macros only as automacro condition. There is no easy other way to check it from a macro.
automacro hook {
   hook packet_privMsg
   save MsgUser
   save Msg
   call {
         log Player $.hooksave0 said $.hooksave1
         }
}
console ("<text>" | /<regexp>/[i])
Triggers when <text> is received on console or the text received matches <regexp>.
The i switch means the regexp is case-insensitive.
Set variables:
  • $.lastLogMsg - the console text that trigerred the automacro.
  • $.lastMatchN - backreference from regexp.
Console generally includes custom strings, like names and chat messages, which can match your regexp, so matching the beginning and the end of the string should almost always be used (learn about regexp to find out how to do it - it's not just matching symbols at the beginning/end). Furthermore, use as narrow character classes as possible (avoid using .* and alike). Otherwise, your macro may be exploited.
Furthermore, format of console output isn't frozen and can change in future versions and/or in translations. It's not a good idea to rely on console condition.
spell [party] <spell> [, ...]
Triggers when someone casts <spell> on you or you are in its scope.
Party support syntax is included (optional). Also trigger on party member (as a target) by other party or monster. Ex: "spell party Lord of Vermilion"
Comma-separated arguments are treated as OR conditions.
Set variables:
  • $.caster - returns the actor (by player/monster) which triggered the last spell syntax on you/party member/in range spell.
  • $.casterName - returns the name of player/monster which triggered the last spell syntax on you/party member/in range spell.
  • $.casterID - returns the ID of player/monster which triggered the last spell syntax on you/party member/in range spell.
  • $.casterPos - returns the last position of player/monster which triggered the last spell syntax on you/party member/in range spell (x y).
  • $.casterSkill - returns the skill name which triggered the last spell syntax on you/party member/in range spell.
  • $.casterTarget - returns the "location of spell" which triggered the last spell syntax on you/party member/in range spell.
  • $.casterTargetName - returns the name of the party member that is in the range of the triggered (as a target) last spell syntax.
  • $.casterDist - returns the distance (between the caster and YOU) which triggered the last spell syntax on you/party member/in range sp


pm ("<text>" | /<regexp>/[i]) [, <player>]
Triggers when <text> is received by PM [from <player>] or the text received matches <regexp>.
The i switch means the regexp is case-insensitive.
Set variables:
  • $.lastpm - the name of the player who PMed you
  • $.lastpmMsg - the message


pubm ("<text>" | /<regexp>/[i]) [, <distance>]
Triggers when a public message [within a distance of <distance>] is received and it is <text> or matches <regexp>
The i switch means the regexp is case-insensitive.
Set variables:
  • $.lastpub - player's name
  • $.lastpubMsg - player's message


party ("<text>" | /<regexp>/[i])
Triggers when <text> is received by party chat or the text received matches <regexp>.
The i switch means the regexp is case-insensitive.
Set variables:
  • $.lastparty - player's name
  • $.lastpartyMsg - player's message


guild ("<text>" | /<regexp>/[i])
Triggers when <text> is received by guild chat or the text received matches <regexp>.
The i switch means the regexp is case-insensitive.
Set variables:
  • $.lastguild - player's name
  • $.lastguildMsg - player's message


system ("<text>" | /<regexp>/[i])
Triggered by system chat.
  • $.lastsysMsg - message


mapchange ( <mapname> | any | * ) [, ...]
Triggers when changing map to <mapname>. If the argument is any or * (asterisk or star) then it triggers on any map change.
Comma-separated arguments are treated as OR conditions.


playerguild (<guild list> [, ...] | <guild.txt>) [, <distance>]
Trigers when guild list (guild1, guild2, ..., guildN) or lists of guilds inside control/guild.txt matches player's guild name when charNameUpdate or player hook-on-demand packets is received. Further discussion could be found at Forums.
If <distance> is not defined, clientSight value will be considered.
Comma-separated guild list are treated as OR conditions.
Set variables:
  • $.lastPlayerID - return the account ID of the player that trigger the syntax
  • $.lastGuildName - return the guild name of the player that trigger the syntax
  • $.lastGuildNameBinID - return the ID of the player that trigger the syntax
  • $.lastGuildNameBinIDDist - return the distance(from you) of the player that trigger the syntax
  • $.lastGuildNameBinIDName - return the player's name that trigger the syntax
  • $.lastGuildNameBinIDJobName - return the job class of the player that trigger the syntax


areaSpell <spell> [<distance>]
Triggers when someone starts casting a ground-targeted skill (centre) that is in the scope of the distance condition from you. If <distance> is not defined, it will consider clientSight value.
Comma-separated arguments are treated as OR conditions.
Set variables:
  • $.areaName - return the spell area effect name
  • $.areaActor - return source actor(Player, Monster and etc etc)
  • $.areaSourceName - return the name of the actor
  • $.areaSourceID - return the binID of the actor
  • $.areaPos - return the centre position/location of the area spell (ex: 123 123 payon)
  • $.areaDist - return the distance of the spell area (centre) to your char
Note. areaSpell automacro syntax Macro 2.0.3-SVN.

States

map <mapname>
Triggers when your current map is <mapname>.
May be used only once per automacro block.
TODO: treat comma-separated arguments as OR condition?


location [not] <mapname [<x1> <y1> [<x2> <y2>]] [, ...]
Triggers when you are [not] at the specified location.
When neither <x1> <y1> nor <x2> <y2> are given, it triggers when you are [not] on <mapname>.
When <x2> <y2> are not given, it triggers when you are [not] on <mapname> at (<x1>,<y1>).
When both <x1> <y1> and <x2> <y2> are defined it triggers when you are on <mapname> somewhere between <x1>, <y1> (upper left) and <x2>, <y2> (lower right, where <x1> < <x2> and <y1> > <y2>
Comma-separated arguments are treated as OR conditions:
location geffen, prontera 123 234
Triggers when you are either in geffen or in prontera at 123 234.
Multiple lines are treated as AND conditions:
location not geffen
location not prontera
Triggers when you are neither in geffen nor in prontera.


hp <condition> <amount>[%]
Triggers when your hp matches the defined condition.
Multiple lines are treated as AND Conditions.


sp <condition> <amount>[%]
Triggers when your sp matches the defined condition.
Multiple lines are treated as AND conditions.


spirit <condition> <amount>
Triggers when your spirits match <condition> <amount>.
Multiple lines are treated as AND conditions.


weight <condition> <amount>[%]
Triggers when your weight matches <condition> <amount> (absolute value) or <condition> <amount> percent (relative value).
Multiple lines are treated as AND conditions.


cartweight <condition> <amount>[%]
Triggers when your cart weight matches <condition> <amount> (absolute value) or <condition> <amount> percent (relative value).
Multiple lines are treated as AND conditions.


zeny <condition> <amount>
Triggers when your zeny amount matches <condition> <amount>.
Multiple lines are treated as AND conditions.


soldout <condition> <slots>
Triggers when the amount of sold out item slots in your shop matches <condition> <slots>.
Multiple lines are treated as AND conditions.


status [not] <status> [, ...]
Triggers when you are [not] <status>.
The statuses "dead" and "muted" are supported additionally.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


inventory "<item>" <condition> <amount> [, ...]
Triggers when you have <condition> <amount> of <item> in your inventory.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


storage "<item>" <condition> <amount> [, ...]
Triggers when you have <condition> <amount> of <item> in your storage.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


cart "<item>" <condition> <amount> [, ...]
Triggers when you have <condition> <amount> of <item> in your cart.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


shop "<item>" <condition> <amount> [, ...]
Triggers when you have <condition> <amount> of <item> in your shop.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


base <condition> <level>
Triggers when your base level matches <condition> <level>.
Multiple lines are treated as AND conditions.


job <condition> <level>
Triggers when your job level matches <condition> <level>.
Multiple lines are treated as AND conditions.


class <job>
Triggers when your job is <job>
Example class Glt. Cross
May be used only once per automacro block.
TODO: treat comma-separated arguments as OR condition?


monster [not] <monster(s) name> [<condition> <distance>]
Triggers when <monster(s) name> is near. If <distance> is not set, it will consider the value from clientSight.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.
Set variables:
  • $.lastMonster - name of the last monster that triggered the automacro.
  • $.lastMonsterPos - triggered monster position. Ex: x, y map_name
  • $.lastMonsterDist - distance between the triggered monster and you.
  • $.lastMonsterID - the index of the triggered monster.
  • $.lastMonsterBinID - the ID of the triggered monster.
  • $.lastMonsterCount - risk point (available only for monster syntax)


notMonster <monster(s) name> [<condition> <distance>]
Equivalent to monster [not].
May be used only once per automacro block. TODO: Why, when monster may be used several times? Why at all, when there is monster not?


aggressives <condition> <number>
Triggers at <number> of aggressives.
Multiple lines are treated as AND conditions.


player ("<player name>" | /<regexp>/[i]) [, <distance> ]
Triggers when <player name> is on screen and not more than <distance> blocks away.
Multiple lines are treated as AND conditions.
Set variables:
  • $.lastPlayerName - name of the last player that triggered the automacro.
  • $.lastPlayerPos - triggered player position. Ex: x, y map_name
  • $.lastPlayerLevel - base level of the last player that triggered the automacro.
  • $.lastPlayerJob - job of the last player that triggered the automacro.
  • $.lastPlayerAccountId - account id of the last player that triggered the automacro.
  • $.lastPlayerBinId - binID/index of the last player that triggered the automacro.


equipped [<slot>] (<item> | none) [, [<slot>] (<item> | none) [, ...]]
Triggers when <item> or none is equipped [in slot <slot>]
Slots are topHead, midHead, lowHead, leftHand, rightHand, robe, armor, shoes, leftAccessory, rightAccessory and arrow.
Each comma-separated argument only checks for its own slot, if it's specified.
Comma-separated arguments are treated as OR conditions.
Multiple lines are treated as AND conditions.


var <variable> (unset | <condition> <value>)
Triggers when <variable> is either unset or matches <condition> <value>.
Multiple lines are treated as AND conditions.
Note: in this parameter specifies ONLY the name of the variable, without the "$" symbol. Example:
automacro variable {
  var test unset
  timeout 5
  call {
    log test is unset: $test
  }
}
macro set_test {
  $test = 1
  log variable \$test = $test
}


varvar <nested variable> (unset | <condition> <value>)
Triggers when <nested variable> is either unset or matches <condition> <value>.
Multiple lines are treated as AND conditions.


whenGround [not] <spell>
Triggers when we are in the effect of ground status.
May be used only once per automacro block. TODO: allow multiple whenGround lines per automacro, to check whether several different ground statuses are affecting?
Comma-separated list will be treated as OR condition


localtime <condition> <time>
Triggers when CPU clock time matches the given <condition> <time>.
Time is on 24h format. Ex: 11:22:33 (hour:minute:second)
Multiple lines are treated as AND conditions.


eval <perl expression>
Triggers when <perl expression> is true.
May be used only once per automacro block. TODO: allow multiple eval lines per automacro?

Special

Other stuff you can write in automacro block. Use maximum of one for each of these, unless specified otherwise. These may be NOT conditions.

overrideAI (0 | 1)
macro_delay <n>
exclusive (0 | 1)
orphan <method>
Already documented options for macro call.
Each may be used only once per automacro block.
run-once (0 | 1)
When set to 1 the automacro will be locked after being triggered.
Use the macro command release to unlock this automacro.
May be used only once per automacro block.
disabled <boolean>
Automacro will be initially locked.
Use the macro command release to unlock this automacro.
May be used only once per automacro block.
delay <n>
Waits for <n> seconds before calling the corresponding macro.
May be used only once per automacro block.
timeout <n>
Waits at least for <n> seconds before this automacro can be triggered again.
May be used only once per automacro block.
priority <num>
Chooses which automacros should be checked before others. The smaller the <num> is, the sooner the automacro gets checked. If priority is not given, the priority is assumed to be 0. (zero: check first).
May be used only once per automacro block.
set <variable> <value>
Sets variable <variable> to <value>. You can have multiple set lines per automacro.

Example

automacro sp {
   location prontera
   run-once 1
   call {
       log i'm on prontera o/
   }
}


Always remember, when opening a {, always close it with a }.

Orphaned Macros

It may happen - for example by using ai clear while a macro is running - that a macro becomes orphaned. That means the macro object exists but cannot continue because it requires the AI queue to contain the entry "macro" (or "deal") at the first place. When the AI queue gets cleared, the "macro" entry vanishes. There are three methods:

terminate terminates the macro (equivalent to macro stop)
reregister re-registers to AI queue, overriding other entries. This means to force the continuation of the macro.
reregister_safe re-registers to AI queue when AI gets idle. This means the macro will continue when all other tasks are done.

Perl Subroutines

Macro plugin support Perl subroutines in macros. You can now create any simple Perl function without limitations from the eval command.

Example No.1 - how to create a Perl Subroutine function in Macro

macro sub {
 $list = Orange, Milk, Soya, Peach
 if (existsInList("$list", "PeAch")) goto ok 
 log Not Match!!!; stop
 :ok
 log Match!!!
 $x = @eval(existsInList("$list", "PeAch"))
 log \$x = $x   # $x here is 1
}
sub existsInList {
  my ($list, $val) = @_;
  return 0 if ($val eq "");
  my @array = split / *, */, $list;
  $val = lc($val);
  foreach (@array) {
     s/^\s+//;
     s/\s+$//;
     s/\s+/ /g;
     next if $_ eq "";
     return 1 if lc eq $val;
  }
  return 0;
}


Example No.2 - how to create a re-writable file function using Perl Subroutine in Macro

automacro confHP1 {
   hp > 85%
   exclusive 1
   run-once 1
   set setting Demon Pungus   #becareful on your case, its case sensitive
   set attack 1
   set teleport 0
   set telesearch 1
   call HP
}
automacro confHP2 {
   hp < 75%
   exclusive 1
   run-once 1
   set setting Demon Pungus
   set attack 1
   set teleport 2
   set telesearch 1
   call HP
}
macro HP {
     #Getting the value of the $setting monster name Ex: $setting $exist1 $exist2 $exist3
       $exist1 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{attack_auto}:"None")
       $exist2 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_auto}:"None")
       $exist3 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_search}:"None")
       log Old Values are $setting $exist1 $exist2 $exist3
       log Changing the values to $setting $attack $teleport $telesearch
       do eval Misc::mon_control("$::Macro::Data::varStack{setting}")->{attack_auto} = $attack; Misc::mon_control("$::Macro::Data::varStack{setting}")->{teleport_auto} = $teleport; Misc::mon_control("$::Macro::Data::varStack{setting}")->{teleport_search} = $telesearch
       log Writting mon_control.txt with new values
       rewrite()  # see the sub-routine function below
       log Reloading mon_control.txt
       do reload mon_control
       $exist1 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{attack_auto}:"None")
       $exist2 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_auto}:"None")
       $exist3 = @eval (defined Misc::mon_control("$setting")?Misc::mon_control("$setting")->{teleport_search}:"None")
       log New mon_control.txt Setting: $setting $exist1 $exist2 $exist3
       log Macro done
    #if $teleport = 0 ; means the Higher automacro HP is currently triggered
    #if $teleport = 2 ; means the Lower automacro HP is currently triggered
       if ($teleport < 2) goto releaseHighHp
       :releaseLowHp
            release confHP1
            stop
       :releaseHighHp
            release confHP2
            stop
}
sub rewrite {
  my $monster = Misc::mon_control("$::Macro::Data::varStack{setting}");
  my @lines = ();
  if (open(FILE, "<:utf8", Settings::getControlFilename("mon_control.txt"))) {
     while (<FILE>) {
        s/\x{FEFF}//g; chomp;
        if (/^#/ || /^\n/ || /^\r/) {
           push @lines,$_;
           next
        }
        /^(\d+|([a-zA-Z' ]+)*) -?\d/;
        if ("$::Macro::Data::varStack{setting}" eq $1 && defined $monster) {
           $_ = $1; s/\s+$//;
           push @lines,$_ . " $monster->{attack_auto} $monster->{teleport_auto} $monster->{teleport_search} $monster->{attack_lvl} $monster->{attack_jlvl} $monster->{attack_hp} $monster->{attack_sp} $monster->{weight}"
        }
        else {push @lines,$_}
     }
     close FILE
  }
  open(FILE, ">:utf8", Settings::getControlFilename("mon_control.txt"));
  print FILE join "\n", @lines;
  close FILE;
}

Comments

The macro files allow comments, i.e. lines that are ignored by the macro plugin.

  • Lines starting with a # will be treated as a comment.
  • Everything after a space and followed by a # will also be ignored.
macro happy {
     # this is a comment line
    log I'm Happy # this is also a comment
}

Will print:

[macro] I'm Happy

Examples with Explanations

I assume you already know how to use Console Commands and understand how they work. If not, before going further, read through all Console Commands and try out ones like a, ai, move, cart get, storage add, talk, deal, take, direction commands, sl, sm, relog, pm and others.

Keep another page with this manual open for cross reference.


Okay, so there are 2 types of macros

  1. automacros – these trigger automatically
  2. macros – these do not trigger automatically but need to be called manually or by an automacro


Automacros

Automacros are macros which automatically trigger when certain conditions are met, just like how blocks in config.txt trigger depending on the conditions set in them.

The point of automacros is that you use them to check for conditions. When the condition is fulfilled, you can either respond to it in the automacro or call a macro to do it. The format for automacro is...

automacro <name> {
        condition 1
        condition 2
        …...
        …...
        call {
                command 1
                command 2
                …..
                …..
        }
        timeout <n seconds> #(if necessary)
}


For example, suppose you're playing manually but your slave priest is on Kore, and you want it to warn you if it's running out of sp, you can use an automacro like this....

automacro sp {
   sp < 200
   call {
      do c sp low
   }
   timeout 10
}


Taking it line by line,

  1. automacro sp { - you need to put automacro to tell kore that its an automacro. Then you put the name of the macro, in this case its “sp”. Then you need to put an opening bracket “{“ which tells Kore that the automacro's code begins there.
  2. sp < 200 – After the “{“, you put the conditions on successive lines. Here, there is only one condition and I think its clear that the condition is that sp should be below 200.
  3. call { – The word “call” tells Kore that now you're going to be putting commands, not conditions. The opening bracket “{“ tells Kore that the commands will start now. These commands are the ones that will get carried out when the conditions are met, here when sp goes below 200.
  4. do c sp low – “do” tells Kore that this is a command, something to be done. After “do”, just leave a space and type in the console command you want to execute exactly the way you do in the Kore console.
  5. } – This closing bracket “}” tells Kore that the commands have ended.
  6. timeout 10 – This isn't a condition or a command, it works the same way it works in normal Kore blocks, so this automacro cannot trigger within 10 seconds of it already having been triggered. I ALWAYS put timeouts, so even if my conditions are faulty, the automacro doesn't spam and mess up whatever my bot is doing. Otherwise if your automacro is messed up then you could die or something, so its better to put an appropriate timeout.
  7. } – This closing bracket “}” tells Kore that your automacro code has ended.

So basically whenever, the bot's sp goes below 200, it says in public chat “sp low”. This also has the advantage of making your bot not look like a bot =p.


Here's another macro which illustrates how you can use macros to handle weird situations which Kore is not equipped to deal with. When fighting against metalings, they often strip you and then your oh-so-smart bot punches! To get around this, put the weapon you use in default auto equip and use this macro.

automacro strip {
   status Strip Weapon
   call {
      do tele
      do relog 10
   }
   timeout 10
}


Taking it line by line....

  1. automacro strip { - Tell Kore this is an automacro, and it's name is "strip". The '{' tells Kore the code of the automacro starts here.
  2. status Strip Weapon - The only condition in this macro. This checks your list of statuses for the status "Strip Weapon". Thus, when you are stripped by a Metaling, you get the "Strip Weapon" status and this condition will be fulfilled.
  3. call { - The word "call" and "{" tells Kore that the commands to be executed start after the '{'
  4. do tele - "do" tells Kore that what comes after is a console command to be carried out. Here, the command is "tele" so you teleport away from the Metaling, so that in case you lag or something, you aren't killed while executing the next instruction which is.....
  5. do relog 10 - Tells Kore to relogin in 10 seconds, because when you login again, your "strip weapon" status is cleared.
  6. } - The closing bracket '}' tells Kore the commands have ended.
  7. timeout 10 - Ensures automacro is not spammed, and 10 seconds is enough time to execute the automacro. So after 10 seconds, if for some reason you are still stripped, this automacro will trigger again. However, if you re-login and are stripped within 10 seconds, then macro will not trigger again untill the 10 seconds are up.
  8. } - The closing bracket '}' tells Kore the macro code has ended.

Summarising, on being stripped, you teleport away from the monster and then relogin, with a timeout of 10 seconds to ensure that the automacro is not spammed. Since the weapon you're using is filled in the default weapon option, after re-logging in, Kore will automatically re-equip that weapon.


Let's try a more complicated macro. If you have a priest, you would want it to warp back to its lockmap or the nearest warp-able map near its lockmap. This macro does it. From the savemap, the bot takes the same route to the lockmap each time. So in the first map outside the town, just set it to move to a designated tile, and cast warp on a nearby tile, and then move to that tile. You will need delays to allow the spell to be cast and commands to be completed.

automacro warp {
   map gef_fild07
   inventory "Blue Gemstone" > 0
   call {
      do ai manual
      pause 1
      do move 319 187
      do sl 27 316 188
      pause 2
      do warp 1
      pause 1
      do move 316 188
      do ai on
   }
   timeout 20
}


Taking it line by line,

  1. automacro warp { - As explained, the automacro's name is warp, and “{“ indicates that the automacro begins.
  2. map get_fild07 - The first condition, the automacro will only trigger if the map is gef_fild07.
  3. inventory “Blue Gemstone” > 0 - Second condition, ensures there is atleast one blue gemstone in the inventory so that warp portal can be cast. Obviously you need a getAuto in config.txt for the gemstones.
  4. call { - Tells Kore that the commands start here.
  5. do ai manual - Frequently, I use this command in the more complicated and longer macros, so that Kore doesn't get diverted into doing something else while your macro is running, for example, attacking a monster or gathering up an item. In this case I'm using it so that the bot doesn't continue walking.
  6. pause 1 - Inserts a delay of 1 second before the next command is carried out, VERY IMPORTANT. If you don't use delays then Kore can and will miss steps in the macro and the result is a mess.
  7. do move 319 187 - The “move” console command is used to move your bot to a set location.
  8. do sl 27 316 188 - The “sl” console command is used to make your priest cast warp portal on a convenient spot near your location, in this case (316,188).
  9. pause 2 - Very Important. A delay of 2 seconds is put, allowing enough time to cast warp portal. If you priest has low dex, this delay should be increased.
  10. do warp 1 - Console command “warp” is used to select memo location 1.
  11. pause 1 - 1 second delay to allow the portal to form.
  12. do move 316 188 - Move to the portal and Voila! You have been warped to the map.
  13. do ai on - You set ai to manual at the beginning of this macro, so now you need to turn it back on.
  14. } - Closing bracket indicates end of commands.
  15. timeout 20 - Ensures that the bot does not spam the automacro. The timeout should be large enough to allow the completion of the automacro, so that the automacro does not trigger again while it is in execution.
  16. } - Closing bracket indicates end of automacro code.

Note the use of appropriate delays and timeouts in this automacro.


The macro plugin also has many useful built-in variables which give you your position for example. It's all listed in the macro manual. I just want to show how to extract your position from these variables.

$.pos gives you your position. Now to get your x and y coordinates the code is,

  $px = @arg ("$.pos", 1)
  $py = @arg ("$.pos", 2)

Here, the x coordinate is the 1st value in $.pos so we have a '1'. Similarly, the y coordinate is the second value in $.pos so we have a '2'.

If you have a “monster” condition in your automacro, $.lastmonsterpos gives the position of that monster. To extract the x and y coordinates the code is,

  $mx = @arg ("$.lastMonsterPos", 1)
  $my = @arg ("$.lastMonsterPos", 2)


"run-once"

Frequently, the run-once condition is used instead of a timeout. It allows the automacro to run only once so that the automacro isn't spammed. To get the automacro to run again, a release command needs to be executed. However, due to a rogue situation that the macro hangs and the release macro isn't executed, your automacro is effectively stuck. To get around this, use a timeout instead of using run-once.


The normal macros

In the 3 examples given, everything was done using automacros. So you're probably wondering what the point of macros is if everything can be done using automacros. Well, not everything can be done using automacros. For example, it is not possible to use macro variables such as $.pos in an automacro. So, if you get errors like “not an automacro variable”, it means that it cannot be done in an automacro but has to be done using a macro. In such a case, the automacro needs to call a macro.


Regular Expressions

Sometimes, in a macro, you need to compare, say the names of characters near you to see if it matches a certain name. This is where regular expressions come in handy. If you're new to macros, you can read this later as it can be a bit complicated, the more advanced regexps can look hellish =P Visit http://www.regular-expressions.info/quickstart.html where it's explained fairly well. I'm just going to put down some basics here.

$.lastpubMsg = /(A|a)uto (S|s)torage/

Here, $.lastpubMsg is your macro variable holding the last public chat message. A regular expression can just be plain text so it could have been /auto storage/ or /auto/ and Kore would have checked if the public chat contained "auto storage" or "auto".

However, here we have used the "|" symbol which means "or". Notice 'A' and 'a' are contained in brackets with a '|' between them. It means both 'auto' and 'Auto' are checked for. Similarly, both 'storage' and 'Storage' are checked for.


Now suppose you want to check for repetition, say 'aauto storage' also, then you may use:

/(a+|A+)uto (S|s)torage/ 

Notice I've used a '+'. This tells Kore that the character it is directly after, in this instance 'a' or 'A', should be present one or more times. So even if someone says "aaaaaaaaaauto storage', it will be valid. Other checks like the '+' are:

  • * matches 0 or more times e.g. /(a*|A*)uto Storage/ so "uto storage" will also work.
  • + matches 1 or more times e.g. as above.
  • ? matches 1 or 0 times e.g. /(a?|A?)uto Storage/ so "uto storage" and "auto storage" work but "aaaauto storage" won't.


So if you want to check for "Kobold" just do /Kobold/ and if "Kobold" is anywhere in the string, it will be valid. For Kobold-1 and Kobold-2 only it will be /Kobold-(1|2)/.

Regexps may also come in handy when you're using the "console /<regexp>/" condition in automacros, if you need to check for a variety of trigger texts.

These are just the basics. I recommend referring to the link provided above if you need to make a more complicated regexp.

How to write a macro

Now that you have some idea of what a macro is, try writing one

  • Figure out under exactly what conditions you want your automacro to trigger
  • Logically think out the steps your bot needs to perform in the automacro. Go step by step over this carefully.
  • Ensure you have inserted appropriate pauses between the steps.
  • Ensure you have a timeout or a run-once in your macro to prevent spamming of it.
  • Now put the code in macros.txt and start the bot or reload macros.txt. If all is fine you won't get any error messages. But if there is a problem in the syntax, say you missed out a “}”, then you will get an error message and the macro won't work. Figure out what the error is, correct it, and reload macros.txt again to check if you corrected it properly or not.

Collected Macros

I would like to share some useful macros, hopefully not angering anyone here.

Identify with Magnifier

You got unidentified Items in your inventory and want to identify them using magnifiers? Use "macro id" on console until all items are identified. You don't have to type anything else, the macro will identify the first item found, after that the second and so on.

macro id {
       $id = @inventory(Magnifier)
       do is $id
       pause 1
       do identify 0
}

Automatically refine Rough Stones (Ori / Elu)

This macro, once called, will automatically walk to the prontera forge and refine Rough Ori / Elu as long as there are more than 5 in your inventory. I did not find something like that yet, please post everything (better) I may have missed.

Just fire macro ref to start. To stop manually, type macro stop.

macro ref {
       do move prt_in 59 60
       call ref-while
}
macro ref-while {
       log start refining with
       $ori = @invamount (Rough Oridecon) R. Oris
       $elu =  @invamount (Rough Elunium) R. Elus
       log $ori $elu
       while (@invamount (Rough Oridecon) > 4) as loop
               do talk 0
               pause 0.8
               do talk resp 0
               call ref-while
       end loop
       while (inventory (Rough Elunium) > 4) as loop
               do talk 0
               pause 0.8
               do talk resp 1
               call ref-while
       end loop
       stop
}

FAQ

I have an automacro that checks for the amount of an item in my inventory / cart to be less than a given value or equal to zero (e.g. inventory "red potion" <= 30) but that automacro triggers also on map change. Why is that so and what should I do?
When you're changing the map all items vanish from your inventory for a short time. That happens with the official client, too. To avoid this, add an additional check for an item that you always carry with you, like inventory "Jellopy" > 0


I get disconnected from map server when running a macro!
The commands are sent too fast. Increase macro_delay or add pause lines between your do commands


I'm getting Malformed UTF-8 character (fatal), what is this?
This error happens when your macros.txt is not saved in UTF-8 encoding.
To solve it, open your macros.txt and if you're using Notepad, when you are going to save, change Encoding to UTF-8. (If that does not help - do not use Notepad.)
If you're using Notepad++ or other text editor, go to Format > UTF-8 (without BOM) and save.


I dont understand english. Where is ru-version?
Вот ссылка на перевод данного мануала на русский.

Author

The first version of the plugin was written by macro arachno. After that, various contributors, such as ezza, daigaku, keplerbr, eternalhavest (creator of the macro debugger to Wx Interface) and technologyguild helped in the development of macro plugin to be what it is today.

Related tools and resources