|
|
(19 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| + | {{revdate}} |
| =APILogic= | | =APILogic= |
| + | '''APILogic''' introduces logical structures (things like IF, ELSEIF, and ELSE) as well as real-time inline math operations, and variable muling to Roll20 command lines. It can test sets of conditions and, depending on the result, include or exclude parts of the command line that actually reaches the chat processor. |
| {{script overview | | {{script overview |
| |name=APILogic | | |name=APILogic |
− | |author=[[timmaugh]]}} | + | |author=[[timmaugh]] |
− | '''APILogic''' introduces logical structures (things like IF, ELSEIF, and ELSE) as well as real-time inline math operations, and variable muling to Roll20 command lines. It can test sets of conditions and, depending on the result, include or exclude parts of the command line that actually reaches the chat processor.
| + | |dependencies={{api-gh|libInline libInline}} |
| + | }} |
| | | |
| For example, given the statement: | | For example, given the statement: |
− | <pre>!somescript {& if a = a} true stuff {& else} default stuff {& end} | + | <pre style="overflow:auto;white-space:pre-wrap;">!somescript {& if a = a} true stuff {& else} default stuff {& end} |
| </pre> | | </pre> |
| …results in the following reaching the chat: | | …results in the following reaching the chat: |
− | <pre>!somescript true stuff | + | <pre style="overflow:auto;white-space:pre-wrap;">!somescript true stuff |
| </pre> | | </pre> |
− | | + | {{Meta-ToolboxNav}} |
| * '''Discussion:''' {{forum|post/9771314/script-apilogic-gives-if-slash-elseif-slash-else-processing-to-other-scripts APILogic thread}} | | * '''Discussion:''' {{forum|post/9771314/script-apilogic-gives-if-slash-elseif-slash-else-processing-to-other-scripts APILogic thread}} |
| * '''File Location:''' {{repo|TimRohr22/Cauldron/tree/master/APILogic APILogic.js}} (submitted to the one-click, but until then, find it in my personal repo)<br> | | * '''File Location:''' {{repo|TimRohr22/Cauldron/tree/master/APILogic APILogic.js}} (submitted to the one-click, but until then, find it in my personal repo)<br> |
− | * '''Script Dependency:''' {{repo|TimRohr22/Cauldron/tree/master/libInline libInline.js}} (also submitted to the one-click)<br> | + | * Documentation for [[Script:APILogic/v1.2.1 (and before)|APILogic 1.2.1(and before)]] |
| | | |
| | | |
− | APILogic exploits a peculiarity of the way many of the scripts reach the chat interface (a peculiarity first discovered by – who else? – The Aaron) to give it the ability to intercept the chat message ''<b>before</b>'' it reaches other scripts, no matter if it is installed before or after them in the script library. It also uses a separate bit of script magic to let it retain ownership of the message even when otherwise asynchronous chat calls would be going. | + | APILogic exploits a peculiarity of the way many of the scripts reach the chat interface (a peculiarity first discovered by – who else? – [[The Aaron]]) to give it the ability to intercept the chat message ''<b>before</b>'' it reaches other scripts, no matter if it is installed before or after them in the script library. It also uses a separate bit of script magic to let it retain ownership of the message even when otherwise asynchronous chat calls would be going. |
| | | |
| '''Caveat:''' The method APILogic utilizes has been tested and shown to work with a large number of scripts. If you find that it doesn’t, you should be reminded that the most foolproof way to ensure proper timing of script execution is to load APILogic in your script library before the other script. But hopefully you’ll find that you don’t need to do that!) | | '''Caveat:''' The method APILogic utilizes has been tested and shown to work with a large number of scripts. If you find that it doesn’t, you should be reminded that the most foolproof way to ensure proper timing of script execution is to load APILogic in your script library before the other script. But hopefully you’ll find that you don’t need to do that!) |
Line 24: |
Line 27: |
| | | |
| '''Credits:''' Created by [[timmaugh]]. Many thanks to [[Aaron|The Aaron]] for lending his expertise on questions I had, and to the other members of the House of Mod for sounding out and working through ideas. | | '''Credits:''' Created by [[timmaugh]]. Many thanks to [[Aaron|The Aaron]] for lending his expertise on questions I had, and to the other members of the House of Mod for sounding out and working through ideas. |
− |
| |
| __TOC__ | | __TOC__ |
| + | {{apiboxRec}} |
| ==Triggering and Usage== | | ==Triggering and Usage== |
− | <p>You won’t invoke APILogic directly by using a particular handle and a line dedicated for the APILogic to detect. Instead, any API call (beginning with an exclamation point: ‘!’) that also includes any IF, DEFINE, MULE, EVAL, EVAL-, or MATH tag somewhere in the line will trigger APILogic to examine and parse the message before handing it off to other scripts.</p>
| + | You won’t invoke APILogic directly by using a particular handle and a line dedicated for the APILogic to detect. Instead, any API call (beginning with an exclamation point: ‘!’) that also includes any IF, DEFINE, MULE, EVAL, EVAL-, or MATH tag somewhere in the line will trigger APILogic to examine and parse the message before handing it off to other scripts. |
− | <p>As mentioned, you are not limited to using APILogic only for calls that are intended for other scripts. There are mechanisms built into the logic that let you output a simple chat message (no API) once you’ve processed all of the logic structures. That means you can use the logic structures in a simple message that was never intended to be picked up by a script, and also in a message that, depending on the conditions provided, might need to be picked up by another script, or alternatively flattened to a simple message to hit the chat log.</p>
| + | |
| + | As mentioned, you are not limited to using APILogic only for calls that are intended for other scripts. There are mechanisms built into the logic that let you output a simple chat message (no API) once you’ve processed all of the logic structures. That means you can use the logic structures in a simple message that was never intended to be picked up by a script, and also in a message that, depending on the conditions provided, might need to be picked up by another script, or alternatively flattened to a simple message to hit the chat log. |
| | | |
| ==The Basic Structures: IF, ELSEIF, ELSE, and END== | | ==The Basic Structures: IF, ELSEIF, ELSE, and END== |
Line 40: |
Line 44: |
| </pre> | | </pre> |
| <p>Each IF and ELSEIF tag include conditions to be evaluated (discussed in a moment). If an IF’s conditions evaluate as <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">true</span>, the subsequent text is included in the command line. While nested IF blocks embedded within that included text are detected and evaluated, no further sibling tags to the initial IF tag are evaluated until the associated END tag. On the other hand, if an IF evaluates to false, evaluation moves to the next logical structure (ELSEIF, ELSE, or END). ELSEIFs are evaluated just as IFs are, with processing passing forward if we find a false set of conditions. If we ever reach an ELSE, that text is included.</p> | | <p>Each IF and ELSEIF tag include conditions to be evaluated (discussed in a moment). If an IF’s conditions evaluate as <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">true</span>, the subsequent text is included in the command line. While nested IF blocks embedded within that included text are detected and evaluated, no further sibling tags to the initial IF tag are evaluated until the associated END tag. On the other hand, if an IF evaluates to false, evaluation moves to the next logical structure (ELSEIF, ELSE, or END). ELSEIFs are evaluated just as IFs are, with processing passing forward if we find a false set of conditions. If we ever reach an ELSE, that text is included.</p> |
− | <h3>Nesting IF Blocks</h3>
| + | ===Nesting IF Blocks=== |
| <p>You can nest IF blocks in other portions of the line to prompt a new set of evaluation for the enclosed text. They can occur in another IF, in an ELSEIF, or in an ELSE. If the outer logic structure passes validation so that the contents are evaluated, the nested IF block will be evaluated. Each IF must have an END, therefore the first END to follow the last IF belongs to that IF. Similarly, all ELSEIF and ELSE tags that follow an IF (until an END is detected) belong to that IF.</p> | | <p>You can nest IF blocks in other portions of the line to prompt a new set of evaluation for the enclosed text. They can occur in another IF, in an ELSEIF, or in an ELSE. If the outer logic structure passes validation so that the contents are evaluated, the nested IF block will be evaluated. Each IF must have an END, therefore the first END to follow the last IF belongs to that IF. Similarly, all ELSEIF and ELSE tags that follow an IF (until an END is detected) belong to that IF.</p> |
− | <pre>{& if } ... {& elseif } ... {& if } ... {& elseif } ... {& end } ... {& else } ... {& end} | + | <pre style="overflow:auto;white-space:pre-wrap;">{& if } ... {& elseif } ... {& if } ... {& elseif } ... {& end } ... {& else } ... {& end} |
| </pre> | | </pre> |
| <p>In the example, an IF-ELSEIF-END block exists in the first ELSEIF of the outer IF block. it will only be evaluated (and can only be included) if the outer IF block fails validation and the ELSEIF passes.</p> | | <p>In the example, an IF-ELSEIF-END block exists in the first ELSEIF of the outer IF block. it will only be evaluated (and can only be included) if the outer IF block fails validation and the ELSEIF passes.</p> |
Line 51: |
Line 55: |
| ====Logical Comparisons==== | | ====Logical Comparisons==== |
| <p>The following logical comparisons are allowed for comparing two items (binary operations):</p> | | <p>The following logical comparisons are allowed for comparing two items (binary operations):</p> |
− | <pre>a = b // equals | + | <pre style="overflow:auto;white-space:pre-wrap;">a = b // equals |
| a != b // does not equal | | a != b // does not equal |
| a > b // is greater than | | a > b // is greater than |
Line 62: |
Line 66: |
| | | |
| ====Sheet Items==== | | ====Sheet Items==== |
− | <p>APILogic currently can retrieve and evaluate attributes, repeating items, and abilities from a character sheet.</p> | + | <p>The specific ability to retrieve sheet items was removed from APILogic and rolled into the Fetch script (also part of the [[Meta-Toolbox]]). Use a Fetch construction to retrieve the sheet item data before APILogic evaluates it as part of condition.</p> |
− | <pre>@|Character Name|attribute
| + | |
− | %|Character Name|ability
| + | |
− | *|Character Name|list|[pattern]|rpt_item_suffix
| + | |
− | </pre>
| + | |
− | <p>In each example, where the character’s name is referenced, you can substitute the character’s ID or the token ID of a token that represents that character.<br>
| + | |
− | For abilities, the text (or action) of the ability will be returned; for attributes and repeating attributes, the current value. To retrieve the “max” value of an attribute or repeating attribute, see <b>Special Tests and Returning “max” or “name”</b>, below.</p>
| + | |
− | <pre>{& if @|Bob the Hirsuite|health > 18 }
| + | |
− | {& if %|-M4qbDlLf8x7lYHaZ4tt|Blast ~ power }
| + | |
− | </pre>
| + | |
− | <h5>Repeating Items</h5>
| + | |
− | <p>Repeating items are accessed by a list name, a pattern that helps to narrow down which item on that list should be returned, and then a suffix to a repeating item as the value to return. For example, Bob the Slayer has a spell list on his character sheet, where every item on that list has sub-attributes of <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">name</span>, <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">spell_level</span>, and <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">prepared</span>. He has the spell “Disintegrating Blast” entered on that list at level 1, 2, and 3. The following call would return the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">spell_level</span> of the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">prepared</span> “Disintegrating Blast”:</p>
| + | |
− | <pre>*|Bob the Slayer|spells|[name = "Disintegrating Blast" prepared]|spell_level
| + | |
− | </pre>
| + | |
− | <p>Patterns are like the conditions of an IF tag for only the sub-attributes of a repeating item – with two minor differences. First, there is no connective <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">&&</span> or <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">||</span> between each condition; the conditions are <b>all</b> considered to be required for the pattern to detect a repeating element (in other words, they all default to <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">AND</span> relationships). Second, while each test can be binary or unary, a unary test (like, “prepared”, just above) implicitly tests for a truthy value from the field beyond just whether it exists. This typically represents an option button or checkbox on a character sheet.</p>
| + | |
− | <p>Last, note that if there is space in the text provided as the right hand side of a comparison, you should enclose it in single quotes, double quotes, or tick marks (see <b>Text as Condition</b>, below).</p>
| + | |
− | <p>If you don’t know the suffixes of the sub-attributes for a repeating list, I suggest you utilize a script like [https://app.roll20.net/forum/post/9097236/script-insertarg-script-preamp-siege-engine-menu-maker-command-line-constructor/ XRay] (part of the InsertArg script) to help identify the naming parts you need.</p>
| + | |
− | <h5>Sheet Item Reminder</h5>
| + | |
− | <p>Remember, Roll20 will parse standard calls to sheet items before the message is handed off to the API, so if you <i>know</i> an attribute exists, for instance, it could be simpler for you to use the standard syntax to trigger the Roll20 parser to derive the value. On the other hand, using the APILogic syntax to access the sheet item provides an implicit test for whether the item exists at all. Rather than derailing the message, that condition simply evaluates as false, and processing continues.</p>
| + | |
− | <h5>Special Tests and Returning “max” or “name”</h5>
| + | |
− | <p>Part of the implicit test performed on a sheet item is whether it exists (it’s hard for an attribute to be 3-or-greater if it doesn’t exist in the first place). Therefore, to test whether an item exists, you need only include it as a unary test in a condition set:</p>
| + | |
− | <pre>{& if @|Bob the Slayer|weaponsmith }
| + | |
− | </pre>
| + | |
− | <p>Other tests can be identified by including characters between the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">@, %,</span> or <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">*</span> and the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">|</span>. For instance, to also test whether the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">weaponsmith</span> attribute is an integer, include an <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">int</span>:</p>
| + | |
− | <pre>{& if @int|Bob the Slayer|weaponsmith }
| + | |
− | </pre>
| + | |
− | <p>The current special tests are:</p>
| + | |
− | <pre>int => integer
| + | |
− | num => number
| + | |
− | </pre>
| + | |
− | <p>There are also special pieces of data that can be returned, utilizing this same position (with or without the special tests).<br>
| + | |
− | <b>MAX:</b> To return the max value for an attribute, use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">max</span>.</p>
| + | |
− | <pre>{& if @intmax|Bob the Slayer|weaponsmith > 10 }
| + | |
− | </pre>
| + | |
− | <p>The above tests the existence of the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">weaponsmith</span> attribute for Bob the Slayer, whether the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">max</span> value is an integer, and whether that <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">max</span> value is over 10.<br>
| + | |
− | <b>NAME:</b> Sometimes (especially for a repeating item), it is helpful to return the name of the thing on the sheet. Used in a definition, this give you an easy way to retrieve the value of a repeating item in other scripts. To get the name of a field from a repeating element, include <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">name</span> in the same position.</p>
| + | |
− | <pre>{& define ([arrowsattr] *name|Bob the Slayer|resources|[resource_name="arrows"]|resource_name ) }
| + | |
− | </pre>
| + | |
− | <p>The above would return, for example:</p>
| + | |
− | <pre>repeating_resources_-M5sYfPs0x3LgGSdmXC1_resource_name
| + | |
− | </pre>
| + | |
− | <p>…representing the attribute for <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">resource_name</span> where the resource represented “arrows”. The name returned is the name of the field referenced as the last element in the structure, whatever that is. So the following would return the name of the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">resource_quantity</span> attribute for the same “arrows” resource:</p>
| + | |
− | <pre>{& define ([arrowsleft] *name|Bob the Slayer|resources|[resource_name="arrows"]|resource_quantity ) }
| + | |
− | </pre>
| + | |
− | <p>For more information on using definitions, see <b>Using DEFINE Tag</b>, below.</p>
| + | |
− | <p><b>ROW:</b> Roll20 lets you use a “rowID shorthand” ($0, $1, etc.) to reference items in a repeating set based on their sorted order. To get the ordinal/sorted position, use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">row</span>.</p>
| + | |
− | <pre>*row|Bob the Slayer|resources|[resource_name=arrows]|resource_name
| + | |
− | </pre>
| + | |
− | <p>The above would return the rowID shorthand, for instance, $0. Using this method, you could manage a left/right resource list and keep the items in sync. If the elements were in the $2 position in both lists, you could extract and define the rowID in a DEFINE tag, then use it elsewhere in the command line for references to the left or right resource list.</p>
| + | |
− | <p><b>ROWNAME:</b> Much like <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">row</span>, <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">rowname</span> leverages the rowID of the repeating item, but instead returns the full name of the attribute using that rowID. To get the name of the item in the repeating set using the rowID, use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">rowname</span>.</p>
| + | |
− | <pre>*rowname|Bob the Slayer|resources|[resource_name=arrows|resource_name
| + | |
− | </pre>
| + | |
− | <p>The above would return something like:</p>
| + | |
− | <pre>repeating_resources_$0_resource_name
| + | |
− | </pre>
| + | |
| | | |
| ====Text as Condition==== | | ====Text as Condition==== |
− | <p>If you need to include space in a bit of text to include as one side of a comparison operation, you should enclose the entire text string in either single quotes, double quotes, or tick marks. With three options available, you should have an option available even if the text you need to include might, itself have an instance of one of those characters. For instance, the following would not evaluate properly, because of the presence of the apostrophe in the word “don’t”:</p>
| + | If you need to include space in a bit of text to include as one side of a comparison operation, you should enclose the entire text string in either single quotes, double quotes, or tick marks. With three options available, you should have an option available even if the text you need to include might, itself have an instance of one of those characters. For instance, the following would not evaluate properly, because of the presence of the apostrophe in the word “don’t”: |
− | <pre>@|Bob the Slayer|slogan ~ 'don't go' | + | <pre style="overflow:auto;white-space:pre-wrap;">@(Bob the Slayer.slogan) ~ 'don't go' |
| </pre> | | </pre> |
| <p>Instead, wrap it in another option for denoting the text:</p> | | <p>Instead, wrap it in another option for denoting the text:</p> |
− | <pre>@|Bob the Slayer|slogan ~ "don't go" | + | <pre style="overflow:auto;white-space:pre-wrap;">@(Bob the Slayer.slogan) ~ "don't go" |
| </pre> | | </pre> |
| <p>This is good to remember if you intend to use Roll20 parsing to retrieve something. For instance, if you want to use the name of the character associated with the Selected token as a condition, you should wrap that in some form of quotes if there is a chance that name will include a space.</p> | | <p>This is good to remember if you intend to use Roll20 parsing to retrieve something. For instance, if you want to use the name of the character associated with the Selected token as a condition, you should wrap that in some form of quotes if there is a chance that name will include a space.</p> |
− | <pre>"@{selected|token_name}" ~ Slayer | + | <pre style="overflow:auto;white-space:pre-wrap;">"@{selected|token_name}" ~ Slayer |
| // will reach the API after Roll20 parsing as... | | // will reach the API after Roll20 parsing as... |
| "Bob the Slayer" ~ Slayer | | "Bob the Slayer" ~ Slayer |
Line 156: |
Line 106: |
| <pre>!somescript {& if ([sanitycheck] @|Bob the Slayer|sanity > 10 ) } {& end} ... | | <pre>!somescript {& if ([sanitycheck] @|Bob the Slayer|sanity > 10 ) } {& end} ... |
| </pre> | | </pre> |
− | <p>Because the END tag follows nearly immediately on the IF tag, no important text is included or excluded from the command line, no matter the result of the test. The IF tag is there simply to force the group to be evaluated and the result stored. This would be a better solution than using a definition if the group was particularly complex since the definition is a simple text replacement operation. Using a named group ensures that the group is only being retrieved and evaluated once (read more in <b>Using DEFINE Tag</b>).</p>
| + | |
| + | Because the END tag follows nearly immediately on the IF tag, no important text is included or excluded from the command line, no matter the result of the test. The IF tag is there simply to force the group to be evaluated and the result stored. This would be a better solution than using a definition if the group was particularly complex since the definition is a simple text replacement operation. Using a named group ensures that the group is only being retrieved and evaluated once (read more in <b>Using DEFINE Tag</b>). |
| | | |
| ===Negation=== | | ===Negation=== |
Line 177: |
Line 128: |
| <pre>{& define ([term1] definition1) ([term2] definition2) ... } | | <pre>{& define ([term1] definition1) ([term2] definition2) ... } |
| </pre> | | </pre> |
− | <p>The <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> refers to what you will use, elsewhere in the command line, to represent the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>. Since the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> is terminated by a parentheses, you do NOT need to enclose it in some form of quotation marks UNLESS you need to include leading or trailing spaces.</p>
| |
− | <p>Since DEFINE replacements are simple text replacement operations, these can be a way to save typing (providing a short <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> to represent a long <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> that will need to be utilized a number of times in a command line). It also provides a way of minimizing work should a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> need to change – giving you only one place to change it instead of many. This means you could define a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">([speaker] Bob the Slayer)</span> term, and use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">speaker</span> anywhere you would refer to the character; then, if you passed that macro language to a fellow [[player]], they would only have to replace the name with their character in one place.</p>
| |
− | <h3>Difference Between Definition and Named Group</h3>
| |
− | <p>As mentioned in the section on using named groups, although the syntax for defining a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> is very similar to naming a group, the two structures are different. If you placed the entirety of a group as a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>, you would be replicating that text anywhere you referenced the associated <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span>, but each time that text was encountered, the group would be evaluated anew. Declaring the name for the group in an IF tag, where that name represents a set of conditions, ensures that those conditions are only evaluated once.</p>
| |
| | | |
− | ==Using the MATH Tag==
| + | The <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> refers to what you will use, elsewhere in the command line, to represent the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>. Since the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> is terminated by a parentheses, you do NOT need to enclose it in some form of quotation marks UNLESS you need to include leading or trailing spaces. |
− | <p>Using the MATH tag, you can drop real-time, inline math calculations into your macro command to have the value rendered before the message is handed off to the intended script.</p>
| + | |
− | <p>The MATH tag is denoted by the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">{& math ... }</span> formation, where the equation to evaluate follows the tag name:</p>
| + | |
− | <pre>{& math (2+3)/4}
| + | |
− | </pre>
| + | |
− | <p>The above would output 1.25.</p>
| + | |
− | <p>You can use numbers, parentheses, known constants, mule variables, inline rolls or roll markers, and math functions as operands in + , - , * , / , and % (modulo) operations (exponentiation is handled in a function):</p>
| + | |
− | <pre>{& math round(sin(90) * pi, 2) / randb(0,4) } | + | |
− | </pre> | + | |
− | <p>The above rounds (to 2 decimal places), the sine of 90 multiplied by pi, then divides that by a random number between 0 and 4.</p>
| + | |
| | | |
− | ===Mule Variables===
| + | Since DEFINE replacements are simple text replacement operations, these can be a way to save typing (providing a short <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> to represent a long <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> that will need to be utilized a number of times in a command line). It also provides a way of minimizing work should a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> need to change – giving you only one place to change it instead of many. This means you could define a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">([speaker] Bob the Slayer)</span> term, and use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">speaker</span> anywhere you would refer to the character; then, if you passed that macro language to a fellow [[player]], they would only have to replace the name with their character in one place. |
− | <p>All variables from all loaded mules are made available to the math processor directly, without the need of using the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">get</span> statement. Of course, since mules are loaded (and variables retrieved) before equations are handed off to the math processor, using the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">get</span> statement will still work. You just don’t have to use it in a MATH tag’s equation. If you have a variable named 'ArmorMod, then the following are functionally equivalent:</p>
| + | |
− | <pre>{& math ArmorMod + 4} | + | |
− | {& math get.ArmorMod + 4}
| + | |
− | </pre> | + | |
| | | |
− | ===Functions=== | + | ===Difference Between Definition and Named Group=== |
− | <p>Most of the javascript Math functions are exposed for you, as well as some that were added to answer common requirements. These include things like round (with a decimal places argument), ceiling, floor, min, max, random, random between, random among, absolute value, square root, cube root, and more. To use them, include the specified name followed by an open parentheses and any arguments as necessary before supplying a closing parentheses. Functions are nestable.</p>
| + | As mentioned in the section on using named groups, although the syntax for defining a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> is very similar to naming a group, the two structures are different. If you placed the entirety of a group as a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>, you would be replicating that text anywhere you referenced the associated <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span>, but each time that text was encountered, the group would be evaluated anew. Declaring the name for the group in an IF tag, where that name represents a set of conditions, ensures that those conditions are only evaluated once. |
− | <p>For a full list of functions included in the math processor, see <b>APPENDIX III</b>.</p>
| + | |
− | <h3>Constants</h3>
| + | |
− | <p>The following constants are available as part of the math processor:</p>
| + | |
− | <ul>
| + | |
− | <li><b>e</b> - Euler’s number (javascript: Math.E)</li>
| + | |
− | <li><b>pi</b> - Pi (javascript: Math.PI)</li>
| + | |
− | <li><b>lntwo</b> - Natural log of 2 (javascript: Math.LN2)</li>
| + | |
− | <li><b>lnten</b> - Natural log of 10 (javascript: Math.LN10)</li>
| + | |
− | <li><b>logtwoe</b> - Base 2 log of e (javascript: Math. LOG2E)</li>
| + | |
− | <li><b>logtene</b> - Base 10 log of e (javascript: Math.LOG10E)<br>
| + | |
− | Constants take priority over Mule variables of the same name, therefore if you have reason to store a variable under the name ‘pi’, for instance, you can only reach it within the math processor by a more specific LCR – including the Mule or Character.Mule information.</li>
| + | |
− | </ul>
| + | |
− | | + | |
− | ==Escaping Text (Deferring Processing)==
| + | |
− | <p>You can use <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">\</span> characters to break up text formations that the Roll20 parser might otherwise recognize and try to process before you are ready. The escape character is removed before the message is released to other scripts.</p>
| + | |
− | <p>For instance, referencing an attribute that does not exist might normally throw an error to the chat output. In that case, you could use escaping to mask the evaluation of the referenced attribute, and only include it in the command line if the attribute exists:</p>
| + | |
− | <pre>!somescript {& if @|Bob the Slayer|smooth_jazz } @\{Bob the Slayer|smooth_jazz} {& end}
| + | |
− | </pre>
| + | |
− | <p>The escape character prevents the Roll20 parser from recognizing the request for the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">smooth_jazz</span> attribute until we’ve determined whether the attribute exists in the first place. If it doesn’t exist, that portion of the line never survives to be included in the final output.</p>
| + | |
− | <p>Here’s another example where that potentially non-existent value would be the basis for the number of dice in an [[Inline Roll|inline roll]]. In this case, both the attribute detection ***and *** the [[Inline Roll|inline roll]] need to be deferred;</p>
| + | |
− | <pre>\[\[ @\{Bob the Slayer|smooth_jazz}d10 \]\]
| + | |
− | </pre>
| + | |
− | | + | |
− | ===Timing===
| + | |
− | APILogic gets the message <b>after</b> the Roll20 parser has already handled things like requests for sheet items, roll queries, and inline rolls. APILogic processes EVAL, MULE, MATH, DEFINE, EVAL-, and IF tags before finally setting any variable values. After all of that work is finished, it un-escapes characters and uses a bit of script magic to invoke the whole process again (including Roll20 parsers to handle the “newly created” detectable items as well as the APILogic test for “newly created” blocks to trigger further processing) before releasing the message to other scripts. See <b>Order of Operations</b>, below, for a fuller breakdown of the sequence.
| + | |
− | | + | |
− | ===Caveat===
| + | |
− | <p>Removing the escape characters happens automatically for any chat message that triggers APILogic to examine the command line (meaning an API call where you used either an IF or DEFINE tag). Because of this, for any message where you would use the IF and/or DEFINE tags and you also need to actually include a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">\</span> character, you will need to escape the escape character: <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">\\</span>. Multiple levels of escaping require the same number of escapes for any slash you wish to keep.</p>
| + | |
− | | + | |
− | ==Post Processing Tags: STOP and SIMPLE==
| + | |
− | <p>Once all of the other processing is handled (escape characters, definition processing, logical construct processing, rebuilding the command line), APILogic performs one last check of the command line, looking for either the STOP or SIMPLE tags.</p>
| + | |
− | <pre>{& stop}
| + | |
− | {& simple}
| + | |
− | </pre>
| + | |
− | <p>If detected, these tags trigger special behavior for APILogic.</p>
| + | |
− | <p>The STOP tag tells APILogic not to release the message. In other words, nothing would reach the chat window. Conversely, the SIMPLE tag tells APILogic to release the message only after it has converted it from an api-call (prepended with the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">!</span>) into a simple chat message (and removing any SIMPLE tags it finds). Embedding these tags within IF blocks give you a way to provide different results for different branches of your condition evaluation.</p>
| + | |
− | <pre>!{&define ([speaker] Bob the Slayer) }{& if @|speaker|smooth_jazz}somescript {&else} Sorry, speaker doesn't have the smooth_jazz attribute{&simple}{&end}
| + | |
− | </pre>
| + | |
− | <p>If the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">smooth_jazz</span> attribute doesn’t exist for Bob the Slayer, then the portion of the command line that gets included <i>also</i> includes the SIMPLE tag, which sends a simple message reporting that fact.</p>
| + | |
− | <pre>Sorry, Bob the Slayer doesn't have the smooth_jazz attribute
| + | |
− | </pre>
| + | |
− | <p>(Notice, we also used a DEFINE tag to define <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">speaker</span> and re-use it later. Also see if you can spot the other trick we used; the method is discussed in the <b>Advanced Usage</b> section.)</p>
| + | |
− | <h2>Inline Rolls</h2>
| + | |
− | <h3>Nesting Inline Rolls</h3>
| + | |
− | <p>APILogic takes advantage of its loop to give you the ability to nest your reused [[Inline Roll|inline rolls]]. For instance, we know that the first inline roll detected by the Roll20 parser can be reused in the command line by using the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]]</span> marker. However, using that marker nested inside another inline roll would break the Roll20 parser and throw an error:</p>
| + | |
− | <pre>[[ $[[0]]d10 ]]
| + | |
− | </pre>
| + | |
− | <p><i>(this breaks in a normal chat call!)</i><br>
| + | |
− | If APILogic detects a nested inline roll, it will drop the marker out and substitute in the value of the roll. To make it work, you should escape the outer roll brackets.</p>
| + | |
− | <pre>\[\[ $[[0]]d10 \]\]
| + | |
− | </pre>
| + | |
− | <p>That way, by the time the Roll20 parsers see the outer roll structure, APILogic will have replaced the inner roll marker with the value of the roll.</p>
| + | |
− | <p>Nest multiple levels of inner rolls by using 1-more escape character for each outer wrapping of inline roll structure.</p>
| + | |
− | ===Getting the Value of an Inline Roll===
| + | |
− | <p>As mentioned just above, any [[inline roll]] detected as nested in another inline roll is automatically converted to its resulting value. This also happens for any inline roll included as part of an IF or ELSEIF’s condition. There may, however, be other times you wish to extract the value of another inline roll in a message being processed by APILogic. When that need arises, you can append <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span> to the closing double bracket structure to have APILogic substitute the resulting value from the roll in place of the roll marker.</p>
| + | |
− | <pre>!somescript {& define ([boneroll] [[1d10]].value) }{& if boneroll > 3} ... {& end}
| + | |
− | </pre>
| + | |
− | <p>You can append the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span> to an inline roll (<span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">[[1d10]].value</span>) or to an inline roll marker (<span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]].value</span>). <b>Note</b> that if you use the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span> to extract the result value from a roll marker, the referenced roll must exist at the time that the value is extracted, or else it will be replaced by 0.</p>
| + | |
− | <p>This is important to remember when deferring the processing of inline rolls. The <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span> text is detected several times during each pass of the APILogic parsing. Therefore, the roll marker must not be recognized until the referenced, deferred roll has occurred.</p>
| + | |
− | <p>This command line will work:</p>
| + | |
− | <pre>!somescript {& if [[1d10]] > 3 } $[[0]].value {& end}
| + | |
− | </pre>
| + | |
− | <p>…because the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]]</span> roll is available when the value is extracted from it. This command (below), on the other hand, will not work:</p>
| + | |
− | <pre>!somescript {& if @|Bob the Slayer|basket_weaving} \[\[ @\{Bob the Slayer|basket_weaving}d10\]\] {& end} $[[0]].value
| + | |
− | </pre>
| + | |
− | <p>APILogic will try to extract the value from the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">0</span> roll in the roll array on the first pass of the parser. At that time, the inline roll hasn’t resolved or occurred, yet. It is only after the command line is unescaped and reexamined by the Roll20 parser that the inline roll is detected. In this case, the roll marker should be deferred, too (<span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$\[\[0\]\].value</span> or <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]]\.value</span>).</p>
| + | |
− | <p>Also note that any/all inline rolls are executed and caught by the Roll20 parser if they are recognized with the Roll20 parsing is invoked (as many times as necessary). These all exists, whether or not they are a part of text that will ultimately be included in the final command line. However, if you defer an inline roll in a section of command line that will never be parsed (for instance, it is in a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">false</span> branch of the IF block), that text will never be unescaped, so that roll will never exist.</p>
| + | |
− | <p>Remember that APILogic is helping you construct the command line based on conditional checks, and if you would normally feed the resulting roll structure in the command line to an API call to another script, the chances are good that the receiving script already knows how to handle the inline rolls and roll markers. Most of the time, then, you shouldn’t have to extract the result value for roll that is part of a command line that will ultimately be picked up by another script.</p>
| + | |
− | <h2>Order of Operations</h2>
| + | |
− | <p>Once APILogic detects that it needs to do work on the message, it performs a loop of processing until it no longer detects that any further parsing is required. The order looks like this:</p>
| + | |
− | <pre>User sends command
| + | |
− | ===== BEGIN LOOP =====
| + | |
− | Roll20 parses (inline rolls, selected/target calls, sheet calls, etc.)
| + | |
− | APILogic picks up the message (if necessary)
| + | |
− | Inline rolls are collected
| + | |
− | MULE (load/get): Mules loaded, variables retrieved
| + | |
− | MATH: Math operations are run
| + | |
− | EVAL: Early eval scriptlets launched
| + | |
− | DEFINE: Definitions created
| + | |
− | EVAL-: Late eval scriptlets launched
| + | |
− | IF: Logical processing
| + | |
− | MULE (set): Variables are set
| + | |
− | ===== END LOOP =====
| + | |
− | STOP/SIMPLE: Prior to releasing the message, these tags are evaluated for intended behaviors
| + | |
− | MESSAGE RELEASED
| + | |
− | </pre>
| + | |
− | <p>The loop is where the importance of deferring parts of the processing becomes apparent.</p>
| + | |
− | <p><b>Note:</b> after the first trip through the loop, other things will contribute to telling APILogic to continue processing. In addition to automatically continuing if it sees an IF, DEFINE, EVAL, EVAL-, MULE, or MATH tag, after the second pass APILogic will also continue if it detects a new inline roll and/or a Mule <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">get</span> or a <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">set</span> statement (any of which would have been deferred to have been only detected at this point).</p>
| + | |
− | | + | |
− | ==Advanced Usage and Tricks==
| + | |
− | | + | |
− | ===Defining Inline Rolls===
| + | |
− | <p>Knowing which inline roll marker (i.e., <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]]</span>) refers to which inline roll can sometimes be confusing, especially for rolls containing rolls in a message that has other rolls containing rolls, or for branches of the logic that don’t exists anymore, or where you deferred an inline roll with escape characters in part of the command line that was never processed.</p>
| + | |
− | <p>A rough description is that Roll20 processes (and numbers) the rolls from innermost-leftmost to outermost-rightmost. Like I said, that can be confusing, especially when you add in APILogic letting you nest roll markers. In that case, the second pass of roll indices will start where the first pass left off, from innermost-leftmost to outermost-rightmost.</p>
| + | |
− | <p>You can shortcut having to parse all of that by using an inline roll in a DEFINE tag <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>, at the proper level of escape for when the roll should process. That way, when the roll in this definition resolves:</p>
| + | |
− | <pre>{& define ([chaosdice] [[2d10!]] ) }
| + | |
− | </pre>
| + | |
− | <p>…the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">chaosdice</span> <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> will be filled with the appropriate roll marker <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span>. In effect, APILogic sees:</p>
| + | |
− | <pre>{& define ([chaosdice] $[[0]] ) }
| + | |
− | </pre>
| + | |
− | <p>…and uses that <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">definition</span> anywhere it sees the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">chaosdice</span> <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">term</span> else where in the command line.</p>
| + | |
− | <h3>Table Result Recursion</h3>
| + | |
− | <p>The normal process of replacing a roll with its value (for instance with the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span> command or by nesting it in a deferred inline roll) will return the table entry for a rollable table. Obviously, you may need to verify through straight input into the chat that the roll you enter will return a table entry instead of a number. For instance, <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">[[ 1t[Armor] ]]</span> will return the item from the table, while <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">[[ 2t[Armor] ]]</span> will return the result of only the first roll against the table, and <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">[[ 1t[Armor] + 2 ]]</span> will return <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">2</span>.</p>
| + | |
− | <p>This, paired with the fact that APILogic searches for newly-formed inline roll formations during each pass of the parsing, means you can leverage this behavior to handle recursive rolls based on table entries. If the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">Armor</span> table has entries of:</p>
| + | |
− | <pre>[[1d10]]
| + | |
− | [[1d10+3]]
| + | |
− | [[2d10r<2-3]]
| + | |
− | [[2d10r<2]]
| + | |
− | </pre>
| + | |
− | <p>…and the following text is encountered in your command line:</p>
| + | |
− | <pre>[[ 1t[Armor] ]].value
| + | |
− | </pre>
| + | |
− | <p>Then whatever result is obtained from rolling against that table will insert another inline roll into the command line, which will be detected and rolled by the Roll20 quantum roller.</p>
| + | |
− | <p>Be aware that the resulting roll will, itself be wrapped in an inline roll marker (<span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">$[[0]]</span>), so if you need to obtain the value from it (and it is not nested in another deferred inline roll) you will need another <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">.value</span>. Obviously, if that value is another table entry, and the table entry points to another inline roll, the process will continue.</p>
| + | |
− | <h3>Conditional non-API Calls (Basic Chat Messages)</h3>
| + | |
− | <p>If you want to just want to leverage logical structures for your simple chat message, so you don’t want to end up with an API call at all, put a SIMPLE tag outside of any logical structure (in text that will always be included in the final, reconstructed command line). In that case, simply begin your message (or your first IF or DEFINE tag) after the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">!</span>.</p>
| + | |
− | <pre>!{& if @|Bob the Slayer|slogan}@\{Bob the Slayer|slogan}{& end} For tomorrow we dine in hell.{& simple}
| + | |
− | </pre>
| + | |
− | <p>If Bob the Slayer has a slogan, this will include that and tack on the extra. Otherwise, it would just output the last portion as a simple chat message.</p>
| + | |
− | | + | |
− | ===Obfuscating the API Handle===
| + | |
− | <p>Since we are interrupting other scripts answering the API message and reconstructing the command line that they see, we can preempt the API handle that would trigger another script to pick up the message. We did this, above, when we had a SIMPLE tag embedded in an IF construct. If we are going to drop the resulting message to a simple chat statement, we probably wouldn’t want the API handle to some other script to be included, so we make its inclusion dependent on the result of some conditions.</p>
| + | |
− | <pre>!{& if @|Bob the Slayer|smooth_jazz}somescript arg1 arg2{&else} Sorry, speaker doesn't have the smooth_jazz attribute{&simple}{&end}
| + | |
− | </pre>
| + | |
− | <p>If <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">smooth_jazz</span> exists for Bob the Slayer, the above example will run the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">somescript</span> script. If it does not exist, the api handle for that script is dropped, but the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">{& simple}</span> tag is included, ensuring that a readable message is sent to the chat window.</p>
| + | |
− | | + | |
− | ===Mules as Static Access Tables===
| + | |
− | <p>Rollable tables on Roll20 do a lot to provide random results from weighted entries, which can be good for things like random encounters or the like, but which aren’t as helpful for times when you know the value from which you need to derive the result. For instance, if a given level of a character’s Stamina has a direct correlation to a mod applied to their activities, you don’t need a randomized result… you need the result directly tied to what the character’s Stamina is when you consult the table. Similarly, some systems have charts built for how rolls map an attack roll to damage. A Mule can fill this gap.</p>
| + | |
− | <p>Construct your Mule as the entries of the table, with the various states of the referenced input as the variable names. For an Encumbrance Mod table that would return a modifier to rolls based on the weight of the items the character was carrying, that might look like:</p>
| + | |
− | <pre>0=0
| + | |
− | 1=0
| + | |
− | 2=-1
| + | |
− | 3=-1
| + | |
− | 4=-1
| + | |
− | 5=-2
| + | |
− | ...etc...
| + | |
− | </pre>
| + | |
− | <p>If the Mule were named “EncumbranceTable”, you can reference that using the character’s CarryWeight attribute like so:</p>
| + | |
− | <pre>... {& mule EncumbranceTable} ... get.@{selected|CarryWeight} ...
| + | |
− | </pre>
| + | |
− | <p>Using a Mule this way, you can also leverage a MATH tag, if the input number needs to be altered at all:</p>
| + | |
− | <pre>get\.{& math @{selected|CarryWeight} + 2*(20-@{selected|Stamina}) }
| + | |
− | </pre>
| + | |
− | <p>The above adds twice the value that the character’s Stamina is below 20 to the CarryWeight before determining which row to retrieve. Also note that since MATH tags are evaluated <i>after</i> <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">get</span> statements, the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">get</span> had to be deferred for one cycle of the loop.</p>
| + | |
− | | + | |
− | ==Development Path==
| + | |
− | <ul>
| + | |
− | <li>Numeric Ranges as Mule Variable Names (i.e., 1-10)</li>
| + | |
− | <li>Including token items as conditions</li>
| + | |
− | <li>SWITCH/CASE tag</li>
| + | |
− | <li><s>MAX/MIN tag</s><br>
| + | |
− | <s>- Levenshtein Distance for approximate names</s></li>
| + | |
− | <li><s>Other special tests for sheet items</s><br>
| + | |
− | <s>- EVAL tag</s></li>
| + | |
− | </ul>
| + | |
| | | |
| ==Change Log== | | ==Change Log== |
− | <p><b>Version 1.0.0</b> - Initial Release<br>
| + | * '''Version 2.0.0''' - Initial Re-release as part of the [[Meta-Toolbox#ZeroFrame|ZeroFrame]]Meta Toolbox |
− | <b>Version 1.0.1</b> ([https://app.roll20.net/forum/permalink/9772610/ link]) - minor bug fix related to inline table resolution<br>
| + | |
− | <b>Version 1.1.0</b> ([https://app.roll20.net/forum/permalink/9787044/ link]) - changed special tests for sheet items to be <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">int</span> and <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">max</span> instead of <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">i</span> and <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">m</span>; changed DEFINE tags to evaluate sheet items; added the ability to return the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">name</span> of a sheet item<br>
| + | |
− | <b>Version 1.1.1</b> ([https://app.roll20.net/forum/permalink/9797691/ link]) - Minor bug fix, added rowname and row returns for repeating items<br>
| + | |
− | <b>Version 1.1.2</b> ([https://app.roll20.net/forum/permalink/9818971/ link]) - Bug fix in the logic engine where numeric conditions were not always properly detected<br>
| + | |
− | <b>Version 1.1.3</b> ([https://app.roll20.net/forum/permalink/9836244/ link]) - Bug fix where inline rolls in a condition were not accessed correctly<br>
| + | |
− | <b>Version 1.2.0</b> (link) - added EVAL tag; added rule registration for scriptlet plugins; added <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">row</span> and <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">rowname</span> as retrievable things for repeating sheet items; added MATH tag for inline math calculations; added MULE tag (and get/set language) to handle variable storage</p>
| + | |
| | | |
− | =APPENDICES=
| + | * '''Version 1.2.1''' - [[Script:APILogic/v1.2.1 (and before)]] - info on older version |
− | ==APPENDIX I - Writing a 3rd-Party Script Plugin==
| + | |
− | <p>The EVAL tag allows for anyone with a little coding experience to provide an infinite number of extensible features. The EVAL tag will run the script as designated, looking first in its bank of registered plugins. If the script isn’t found there, APILogic will send the script call to the chat to have the script fire that way.</p>
| + | |
− | <p><b>Remember</b>, only plugins registered to APILogic are handled in sequence, with their result substituted into the command line. If nothing is returned, an empty string will be substituted in place of the EVAL block. Only after the plugin code finishes does APILogic take over again. Calls to outside scripts, on the other hand, are not guaranteed to finish before APILogic moves on.</p>
| + | |
− | <p>So, how do you write a scriptlet and register it to APILogic?</p>
| + | |
| | | |
− | ===Accept a Message===
| |
− | <p>A plug-in for APILogic should accept a message object, just as any function that answers a chat event (i.e., handleInput). In fact, your script can <i>also</i> answer a chat event if you like (more on that under <b>Who Called?</b>). The message object will be identical to a message that would be received from a user – it will have properties of <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">who</span>, <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">playerid</span>, <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">content</span>, etc. If there were any inline rolls, it will have an <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">inlinerolls</span> array. This will be a <i><b>copy</b></i> of the message data that is in APILogic, with the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">content</span> replaced to be the reconstructed command line that the user would have sent had they invoked your script directly from chat.</p>
| |
− | <p>In other words, the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">withinrange</span> script might require a command line like the following, if it were to be invoked from the chat interface:</p>
| |
− | <pre>!withinrange 3 -M1234567890abcdef ,
| |
− | </pre>
| |
− | <p>When a user places that in an EVAL tag block, they would write:</p>
| |
− | <pre>{& eval}withinrange(3 -M1234567890abcdef ,){& /eval}
| |
− | </pre>
| |
− | <p>If APILogic detects <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">withinrange</span> as a registered plugin in that game, it will hand off a message with the former command line.</p>
| |
− | <p>Accepting a message might look like this:</p>
| |
− | <pre>const withinrange = (m) => {
| |
− | log(m.who); // logs who sent the message
| |
− | };
| |
− | </pre>
| |
− |
| |
− | ===Parse the content String===
| |
− | <p>As you would with any script, parse the command line to extract the data you require to perform your calculations. If you intend to allow the scriptlet to be called from the command line, make sure that you verify ownership of the message, as well. This might look like:</p>
| |
− | <pre>const withinrange = (m) => {
| |
− | // verify ownership
| |
− | if (m.type !== 'api' || !/^!withinrange\s/.test(m.content)) return;
| |
− | // parse arguments
| |
− | let [range, sourcetoken, delim] = m.content.split(' ').slice(1);
| |
− | log(range);
| |
− | log(sourcetoken);
| |
− | log(delim);
| |
− | };
| |
− | </pre>
| |
− |
| |
− | ===Perform Calculations and Return===
| |
− | <p>Code as you normally would to calculate and arrive at the data you are looking for. When you are done, if you want something to be substituted into the original command line (where APILogic called your plugin), return a string, number, bigint, or boolean. Anything else (included no return or an undefined return) will be replaced with an empty string.</p>
| |
− | <h4>Who Called?</h4>
| |
− | <p>A message that comes from APILogic to a plugin scriptlet will have one property that a chat-interface-generated or API-generated call will not have: <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">apil</span>. If you want your script to answer both a straight invocation as well as an APILogic invocation, you can differentiate your return based on if you detect this property.</p>
| |
− | <p>For instance, if a user invokes <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">withinrange</span> from the chat interface, maybe we want to display a small panel of information regarding the tokens that are in the specified range – including their image, name, etc. However, if the call comes from APILogic, you only want to return the token IDs in a delimited string. In that case, once you have arrived at the data, you could test for existence of the <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">apil</span> property, and return accordingly:</p>
| |
− | <pre>let tokens = getTheTokens();
| |
− | if (m.apil) return tokens.join(delim);
| |
− | // if the code continues, you're dealing with a direct invocation
| |
− | // so proceed to build the panel output...
| |
− | </pre>
| |
− | <h3>Register to APILogic</h3>
| |
− | <p>The step that turns your script into an APILogic plugin is when your script implements the APILogic.RegisterRule() function in an <span style="background-color: rgb( 245 , 245 , 245 ) ; color: rgb( 51 , 51 , 51 ) ; font-family: "menlo" , "monaco" , "consolas" , "courier new" , monospace ; font-size: 13px">on('ready'...)</span> block. Here is an example:</p>
| |
− | <pre>on('ready', () => {
| |
− | try {
| |
− | APILogic.RegisterRule(withinrange);
| |
− | } catch (error) {
| |
− | log(error);
| |
− | }
| |
− | });
| |
− | </pre>
| |
− | <p>The RegisterRule() function can take any number of functions as parameters, so tack on as many plugins as you’ve written:</p>
| |
− | <pre>APILogic.RegisterRule(withinrange, getclosest, getpageforchar);
| |
− | </pre>
| |
− |
| |
− | ==APPENDIX II - Included Script Plugins==
| |
− | The following Script Plugins are included as a part of the APILogic script. If you find a script plugin that you use to be quite helpful, it can be rolled into the included library of plugins for a future release of APILogic.
| |
− | ===getDiceByVal===
| |
− | Retrieves a subset of dice from an inline roll based on testing them against a series of pipe-separated value ranges. Outputs either a count of the number of dice (the default), or a total of the dice, or a delimited list of the dice values (delimiter default is a comma).
| |
− | <pre>===== EXAMPLE SYNTAX =====
| |
− | getDiceByVal( $[[0]] <=2|6-7|>10 total)
| |
− | </pre>
| |
− | <p>The above would retrieve dice from the first inline roll ($[[0]]) that were either less-than-or-equal-to 2, between 6 and 7 (inclusive), or greater than 10. It would output the total of those dice.</p>
| |
− | <p>If you choose a list output, the default delimiter is a comma. You can alter this by using a pipe character followed by the delimiter you wish to include. If your delimiter includes a space, you must enclose it in either single-quotation marks, double-quotation marks, or tick characters.</p>
| |
− | <pre>getDiceByVal( $[[1]] 1|3|5|7|9 list)
| |
− | </pre>
| |
− | <p>The above would output a comma-separated list of odd value die results from the second inline roll ($[[1]]). The following table shows how the delimiter changes based on altering the ‘list’ argument:</p>
| |
− | <pre>ARG | EXAMPLE OUTPUT
| |
− | ------------|---------------------
| |
− | list | 3,7,5,9
| |
− | list|", " | 3, 7, 5, 9
| |
− | list|+ | 3+7+5+9
| |
− | list|` + ` | 3 + 7 + 5 + 9
| |
− | list| | 3759
| |
− | </pre>
| |
− |
| |
− | ===getDiceByPos===
| |
− | <p>Retrieves a subset of dice from an inline roll based on testing them against a series of pipe-separated position ranges. Outputs either the total of the number of dice (the default), or a count of the dice (seems pointless, but it’s available), or a delimited list of the dice values (delimiter default is a comma). Dice position is 0-based, so the first die is in position 0, the second in position 1, etc.</p>
| |
− | <pre>===== EXAMPLE SYNTAX =====
| |
− | getDiceByPos( $[[0]] <=2|6)
| |
− | </pre>
| |
− | <p>The above would retrieve dice from the first inline roll ($[[0]]) that were in positions 0, 1, 2, or 6. It would output the total of those dice.</p>
| |
− | <p>The same guidelines apply for the list delimiter as for the <i>getDiceByVal</i> plugin, above.</p>
| |
− |
| |
− | ==APPENDIX III - Included Math Functions==
| |
− | <p>The following functions are available as part of the Math processor. Feel free to suggest others if you think one would be helpful.</p>
| |
− | <ul>
| |
− | <li><b>abs(x)</b> Returns the absolute value of <i>x</i></li>
| |
− | <li><b>acos(x)</b> Returns the arc-cosine of <i>x</i></li>
| |
− | <li><b>asin(x)</b> Returns the arc-sine of <i>x</i></li>
| |
− | <li><b>asinh(x)</b> Returns the hyperbolic arc-sine of <i>x</i></li>
| |
− | <li><b>atan(x)</b> Returns the arc-tangent of <i>x</i></li>
| |
− | <li><b>atanh(x)</b> Returns the hyperbolic arc-tangent of <i>x</i></li>
| |
− | <li><b>atantwo(x, y)</b> Returns the arc-tangent of the quotient of the arguments (<i>x</i>, <i>y</i>)</li>
| |
− | <li><b>cbrt(x)</b> Returns the cube root of <i>x</i></li>
| |
− | <li><b>ceiling(x)</b> Returns the smallest integer larger than <i>x</i>
| |
− | <ul>
| |
− | <li>-2.1 => -2</li>
| |
− | <li>2.1 => 3</li>
| |
− | </ul>
| |
− | </li>
| |
− | <li><b>cos(x)</b> Returns the cosine of <i>x</i></li>
| |
− | <li><b>cosh(x)</b> Returns the hyperbolic cosine of <i>x</i></li>
| |
− | <li><b>exp(x)</b> Returns Euler’s constant (<i>e</i>), the base of the natural log, raised to <i>x</i></li>
| |
− | <li><b>expmone(x)</b> Returns 1 subtracted from the value of Euler’s constant (<i>e</i>) raised to <i>x</i></li>
| |
− | <li><b>floor(x)</b> Returns the largest integer less than <i>x</i>
| |
− | <ul>
| |
− | <li>-2.1 => -3</li>
| |
− | <li>2.1 => 2</li>
| |
− | </ul>
| |
− | </li>
| |
− | <li><b>hypot(x[, y [, …]])</b> Returns the square root of the sum of the squares of its arguments</li>
| |
− | <li><b>log(x)</b> Returns the natural logarithm of <i>x</i></li>
| |
− | <li><b>logonep(x)</b> Returns the natural logarithm of 1 + <i>x</i></li>
| |
− | <li><b>logten(x)</b> Returns the base-10 logarithm of <i>x</i></li>
| |
− | <li><b>logtwo(x)</b> Returns the base-2 logarithm of <i>x</i></li>
| |
− | <li><b>min([x[, y[, …]]])</b> Returns the smallest value of 0 or more numbers</li>
| |
− | <li><b>max([x[, y[, …]]])</b> Returns the largest value of 0 or more numbers</li>
| |
− | <li><b>pow(x, y)</b> Returns the value of <i>x</i> raised to the <i>y</i> power</li>
| |
− | <li><b>rand()</b> Returns a pseudo-random number between 0 and 1</li>
| |
− | <li><b>randa(x[, y[, …]])</b> Returns a random element from a list of 1 or more numbers</li>
| |
− | <li><b>randb(x, y)</b> Returns a pseudo-random number between <i>x</i> (inclusive) and <i>y</i> (inclusive)</li>
| |
− | <li><b>randib(x, y)</b> Returns a pseudo-random integer between <i>x</i> and <i>y</i>, where the lesser value is inclusive and the larger value is exclusive</li>
| |
− | <li><b>round(x, y)</b> Returns <i>x</i> rounded to <i>y</i> decimal places</li>
| |
− | <li><b>sin(x)</b> Returns the sine of <i>x</i></li>
| |
− | <li><b>sinh(x)</b> Returns the hyperbolic sine of <i>x</i></li>
| |
− | <li><b>sqrt(x)</b> Returns the square root of <i>x</i></li>
| |
− | <li><b>tan(x)</b> Returns the tangent of <i>x</i></li>
| |
− | <li><b>tanh(x)</b> Returns the hyperbolic tangent of <i>x</i></li>
| |
− | <li><b>trunc(x)</b> Returns the integer portion of <i>x</i>
| |
− | <ul>
| |
− | <li>-2.1 => -2</li>
| |
− | <li>2.1 => 2</li>
| |
− | </ul>
| |
− | </li>
| |
− | </ul>
| |
| =Related Pages= | | =Related Pages= |
− | * [[API:Script Index]] Other avaiable APIs | + | * [[API:Script Index]] Curated list of Roll20 APIs |
− | ** {{forum|permalink/9770117/ libInline}} - provides an easy interface for inlinerolls | + | ** '''[[Meta-Toolbox]]''' - collection of powerful APIs |
| + | ** {{forum|permalink/9770117/ libInline}} - provides an easy interface for [[Inline|inline rolls]] |
| ** {{forum|permalink/9097236/ InsertArg}} -- script preamp, siege engine, menu maker, command line constructor | | ** {{forum|permalink/9097236/ InsertArg}} -- script preamp, siege engine, menu maker, command line constructor |
− | ** {{forum|permalink/9817678/ SelectManager}} -- Utility to Preserve the Selected Tokens For API-Generated Calls | + | ** [[SelectManager]] -- Utility to Preserve the Selected [[Tokens]] For API-Generated Calls |
| * [[Complete Guide to Rolls & Macros]] | | * [[Complete Guide to Rolls & Macros]] |
| ** [[Inline Rolls]] | | ** [[Inline Rolls]] |
| ** [[Order of Operations]] | | ** [[Order of Operations]] |
− | <br>
| + | |
− | <br>
| + | |
| [[Category:User API Scripts]] | | [[Category:User API Scripts]] |
| + | [[Category:API Meta Scripts]] |
| + | [[Category:Featured articles]] |
Also, although it requires the API, it is not only for API messages. You can use these logic structures with basic chat messages, too. This document will show you how.
You won’t invoke APILogic directly by using a particular handle and a line dedicated for the APILogic to detect. Instead, any API call (beginning with an exclamation point: ‘!’) that also includes any IF, DEFINE, MULE, EVAL, EVAL-, or MATH tag somewhere in the line will trigger APILogic to examine and parse the message before handing it off to other scripts.
As mentioned, you are not limited to using APILogic only for calls that are intended for other scripts. There are mechanisms built into the logic that let you output a simple chat message (no API) once you’ve processed all of the logic structures. That means you can use the logic structures in a simple message that was never intended to be picked up by a script, and also in a message that, depending on the conditions provided, might need to be picked up by another script, or alternatively flattened to a simple message to hit the chat log.
An IF begins a logical test, providing conditions that are evaluated. It can be followed by any number of ELSEIF tags, followed by zero or 1 ELSE tag. Finally, an IF block must be terminated with an END tag. Each of these are identified by the {& type ... } formation. For instance:
{& if … }
{& elseif …}
{& else}
{& end}
A properly structured IF block might look like this:
Each IF and ELSEIF tag include conditions to be evaluated (discussed in a moment). If an IF’s conditions evaluate as true, the subsequent text is included in the command line. While nested IF blocks embedded within that included text are detected and evaluated, no further sibling tags to the initial IF tag are evaluated until the associated END tag. On the other hand, if an IF evaluates to false, evaluation moves to the next logical structure (ELSEIF, ELSE, or END). ELSEIFs are evaluated just as IFs are, with processing passing forward if we find a false set of conditions. If we ever reach an ELSE, that text is included.
You can nest IF blocks in other portions of the line to prompt a new set of evaluation for the enclosed text. They can occur in another IF, in an ELSEIF, or in an ELSE. If the outer logic structure passes validation so that the contents are evaluated, the nested IF block will be evaluated. Each IF must have an END, therefore the first END to follow the last IF belongs to that IF. Similarly, all ELSEIF and ELSE tags that follow an IF (until an END is detected) belong to that IF.
In the example, an IF-ELSEIF-END block exists in the first ELSEIF of the outer IF block. it will only be evaluated (and can only be included) if the outer IF block fails validation and the ELSEIF passes.
Each IF and ELSEIF must have at least one condition to evaluate. A condition can be either binary (i.e., a = b), or unary (i.e., c), and each element of a condition (the a, b, or c) can be a sheet item, text, inline roll, or a previously evaluated condition set (more on this in a moment).
The following logical comparisons are allowed for comparing two items (binary operations):
The specific ability to retrieve sheet items was removed from APILogic and rolled into the Fetch script (also part of the Meta-Toolbox). Use a Fetch construction to retrieve the sheet item data before APILogic evaluates it as part of condition.
If you need to include space in a bit of text to include as one side of a comparison operation, you should enclose the entire text string in either single quotes, double quotes, or tick marks. With three options available, you should have an option available even if the text you need to include might, itself have an instance of one of those characters. For instance, the following would not evaluate properly, because of the presence of the apostrophe in the word “don’t”:
This is good to remember if you intend to use Roll20 parsing to retrieve something. For instance, if you want to use the name of the character associated with the Selected token as a condition, you should wrap that in some form of quotes if there is a chance that name will include a space.
Multiple conditions can be used for each IF or ELSEIF tag. Use && to denote and AND case, and use || to denote an OR case.
Conditions are evaluated left to right by default. Use parentheses to enclose groups to force those conditions to evaluate as a group before being compared to sibling conditions:
Multiple levels of grouping can be used, provided each sibling element (whether group or condition) is connected with && or ||:
The reasoning behind why you would want to include a part of your command line might be needed at several times in your command line. In that case, you should name your condition group so that you can simply refer to that name later. Name a group by including a bracketed word (no space) after the opening parentheses, before any non-whitespace character:
The above would store the result of the condition (whether Bob the Slayer’s sanity was over 10) as sanitycheck, available to be used later in the command line, including in future, deferred processing (disucssed later).
If you find yourself in this position, you can either investigate a definition (see Using DEFINE Tag, below), or using a root-level IF tag with a single space of dependent text (that is, providing little alteration to your command line, regardless of if it passes).
Because the END tag follows nearly immediately on the IF tag, no important text is included or excluded from the command line, no matter the result of the test. The IF tag is there simply to force the group to be evaluated and the result stored. This would be a better solution than using a definition if the group was particularly complex since the definition is a simple text replacement operation. Using a named group ensures that the group is only being retrieved and evaluated once (read more in Using DEFINE Tag).
Negation can be applied to any element of a condition or to any group by use of the ! character. This can be handy to test for the non-existence of a sheet item:
Note that if you use negation at the same time you are naming a group, the group will evaluate and the result will be stored as with the name. Negation will then return the opposite of the stored value:
A DEFINE tag is a way to provide definitions for terms that you will then use in text replacements throughout your command line. A DEFINE tag can come anywhere in your command line, and is parsed out before any processing of logical constructs occurs. A DEFINE tag is structured like this:
Since DEFINE replacements are simple text replacement operations, these can be a way to save typing (providing a short term to represent a long definition that will need to be utilized a number of times in a command line). It also provides a way of minimizing work should a definition need to change – giving you only one place to change it instead of many. This means you could define a ([speaker] Bob the Slayer) term, and use speaker anywhere you would refer to the character; then, if you passed that macro language to a fellow player, they would only have to replace the name with their character in one place.
As mentioned in the section on using named groups, although the syntax for defining a term is very similar to naming a group, the two structures are different. If you placed the entirety of a group as a definition, you would be replicating that text anywhere you referenced the associated term, but each time that text was encountered, the group would be evaluated anew. Declaring the name for the group in an IF tag, where that name represents a set of conditions, ensures that those conditions are only evaluated once.