Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Trigger a Script after rolling beneath a total certain number.

Hi, I've been trying to find this to no avail. I have a 2d6 system I'm using. Rolling a Total of 6 or lower is a failure. Anyone can click on their token and use the roll macro. I'm using an API script to edit character attributes to add a point to "experience", to get a running total per game. I'm trying to combine them to add the failure experience if a player rolls a 6 or lower to keep players from manually adding a point every single time with a separate Macro. Looking for a rolling total. Here is my Macro for a roll. &{template:default } {{name= @{selected|character_name}  CAST A SPELL  }}  {{Magic:=[[2d6+@{selected|Magic}-?{Embarassed|No, 0|Yes, 2}+?{Select Modifier|None, 0|Forward 1, 1|Forward 2, 2|Backward 1, -1|Backward 2, -2}]]    }} {{On a 10+, the spell succeeds. On a 7-9, the spell succeeds, but there are side effects. (Choose two) • One of the other players takes a condition from a partial misfire (the other players choose). • The spell is different than what you were expecting. • Someone or something is alerted to the casting of the spell. • Your wand is thrown from your hands and lands nearby.  }} and this is the attribute changing ChatSetAttr ver 1.1 !setattr --silent --sel --mod --experience|1 I would like to set the attribute change in the macro to let it change their experience IF they roll lower or equal to a 6. Any Ideas?
1679699523
timmaugh
Pro
API Scripter
It should work with metascripts... Though I'm not at my laptop to test (waitinnnnnnnng for pizza... Stillllll) =D The basic idea would be to use APILogic to test your conditional (is it less than/equal to 6, is it 7-9, or 10+)... Then build your command line frome there. You'd need ZeroFrame to output your template, too. And, if you have to batch up the commands (pairing a template with a second line to issue the ChatSetAttr call, then we might need Fetch, too (ZeroFrame handles the batching, but because we're working with a template, we have to Fetch it in so we don't break the syntax. When I'm back in front of my laptop, I'll try to pop ost an example.
1679700239
GiGs
Pro
Sheet Author
API Scripter
You can use the script approach. If you are designingthe characetr sheet, this also looks like an opportunity for Custom Roll parsing, which lets you combine the features of roll buttons and sheet workers. It's not easy to write though.
1679717871
timmaugh
Pro
API Scripter
OK, here is an example of your command line rebuilt using APILogic and ZeroFrame... !{&if $[[0]].value <= 6}setattr --silent --sel --mod --experience|1 {&else}{&template:default }{{name= @{selected|character_name} CASTS A SPELL }} {{Magic:=[[2d6+@{selected|Magic}-?{Embarassed|No, 0|Yes, 2}+?{Select Modifier|None, 0|Forward 1, 1|Forward 2, 2|Backward 1, -1|Backward 2, -2}]] }} {{Result={&if $[[0]].value <= 9}Side Effects}}{{Choose two= • One of the other players takes a condition from a partial misfire (the other players choose). • The spell is different than what you were expecting. • Someone or something is alerted to the casting of the spell. • Your wand is thrown from your hands and lands nearby.}}{&else}Success!}} {&end}{&simple} {&end} And here is what it looks like. Note that in this image, I got the three options in succession, but to demonstrate that the <=6 branch of the logic had run, I replaced the ChatSetAttr command with a simple straight-to-chat statement. I'll break down the components of the command into different lines so that they're easily to spot and identify, but you can't have line breaks in the final version (unless they come within a template part -- ie, between the double braces). ! {&if $[[0]].value <= 6}     setattr --silent --sel --mod --experience|1 {&else}     {&template:default }         {{name= @{selected|character_name} CASTS A SPELL }}         {{Magic:=[[2d6+@{selected|Magic}-?{Embarassed|No, 0|Yes, 2}+?{Select Modifier|None, 0|Forward 1, 1|Forward 2, 2|Backward 1, -1|Backward 2, -2}]] }}         {{Result=             {&if $[[0]].value <= 9}                     Side Effects}}                 {{Choose two= • One of the other players takes a condition from a partial misfire (the other players choose).                     • The spell is different than what you were expecting.                     • Someone or something is alerted to the casting of the spell.                     • Your wand is thrown from your hands and lands nearby.}}             {&else}                 Success!}}             {&end} {&simple} {&end}
Oh! Both of these are great ideas. Thanks for the advice and example, this is a great help. 
Quick question if I may... timmaugh said: OK, here is an example of your command line rebuilt using APILogic and ZeroFrame... !{&if $[[0]].value <= 6}setattr --silent --sel --mod --experience|1 {&else}{&template:default }{{name= @{selected|character_name} CASTS A SPELL }} {{Magic:=[[2d6+@{selected|Magic}-?{Embarassed|No, 0|Yes, 2}+?{Select Modifier|None, 0|Forward 1, 1|Forward 2, 2|Backward 1, -1|Backward 2, -2}]] }} {{Result={&if $[[0]].value <= 9}Side Effects}}{{Choose two= • One of the other players takes a condition from a partial misfire (the other players choose). The idea is to determine the value of a 2d6 roll which will have different results accordingly. How does $[[0]].value connect with the [[2d6+@{selected|Magic}-?{Embarassed|No, 0|Yes, 2}+?{Select Modifier|None, 0|Forward 1, 1|Forward 2, 2|Backward 1, -1|Backward 2, -2}]] part? Is $[[0]].value a type of variable? How does the script know where the value is located so it can retrieve it (even, seemingly, before the roll is executed)? Thanks!
1680053789

Edited 1680053883
timmaugh
Pro
API Scripter
Hey, Jean-Guy... .value is a ZeroFrame syntax structure which can extract the value of a given roll. When you submit a command line, the inline rolls are converted to roll markers... basically they are replaced with the index of the roll (numbered as the parser encounters and closes the rolls). So this: !scripthandle --roll|[[1d10]] --roll|[[1d20]] ...becomes: !scripthandle --roll|$[[0]] --roll$[[1]] Most of the time, scripts know how to handle an inline roll like this (drawing from the roll marker the index of the roll they want to look up), but when you want to use the value in a conditional check or you want to reuse the roll in another roll (something you can't do in native Roll20 syntax), you have to be able to get the value of the roll. We do that by appending the .value structure to the appropriate roll marker: $[[0]].value $[[1]].value etc. (There is a logic to the order rolls are encountered, but that is a little beyond the scope of this post.) So when we start the command line as: !{&if $[[0]].value <= 6} ...we are extracting the value of the 0 th roll (the first roll encountered). The roll is available (even though it occurs later in the command line) because by the time scripts get the message, the command line has been through the Roll20 parsers (replacing the rolls with the roll markers). At the same time, the roll data is attached to the message object. So by the time ZeroFrame goes to extract the value from the 0 th roll, the roll data is already available.
1680058200

Edited 1680058272
Thanks for the information Tim, it will certainly be useful. Also, as I was reading your example, it gave me an idea on how to do something I wanted to do and was having some diffculty with. The thing is that it combines script calls ( ChatSetAttr ) and Chat Menu text, all wrapped up in APILogic . I tried a few things, without much success, and I was sleeping on it. When I saw your example it helped and I got it working. However, I used two distinct {&if} blocks, but both blocks are validating the same condition. I was having trouble because I could not find the correct way of placing the {&simple} tag. So this works: ! {&if @{selected|Heroes} = 0} modattr --silent --charid @{selected|character_id} --mabtempmod|1 --rabtempmod|1 --Bless|1 !!! {&end} {&if @{selected|Heroes} = 0} /w Réjean &{template:default} {{name=**Activate Bless**}}{{Bless Result:=**Base Attack Bonus** has been increased by 1 for *@{selected|character_name}*.}} {&else} /w Réjean &{template:default} {{name=**Activate Bless**}}{{Bless Result:=**Bless** cannot be activated for *@{selected|character_name}* because *Heroes' Feast* is already active.}} {&end}{&simple} Is there a way to combine the two blocks, or is that the only way to do it when you have simple text output to the chat menu and script commands as well? Thanks!
1680105131

Edited 1680134720
timmaugh
Pro
API Scripter
Hmm... with the new batching ability of ZeroFrame, I find it easier to write ChatSetAttr commands as their own full line rather than the embedded form (where the command is between ! and !!!). With a batched command line, you don't even need the {&simple} anymore, because ZeroFrame assumes that if the line does not start with a !, then it should be output to chat. If I went that way, then no sub-command (one of the lines we batch up) can have a closing double brace in it, so I'd save your template command line as its own ability. Let's call it ActivateBless and put it on Réjean. That ability will read like this: /w Réjean &{template:default} {{name=**Activate Bless**}} {&if @(selected.Heroes) = 0}{{Bless Result:=**Base Attack Bonus** has been increased by 1 for *@(selected.character_name)*.}}  {&else} {{Bless Result:=**Bless** cannot be activated for *@(selected|character_name)* because *Heroes' Feast* is already active.}}  {&end} Note that ability will not be able to run on its own because of how we have to structure it in order to make it work in our batched line. But... aircoding... I think this should then work for your original command line: !{{   !{&if @{selected|Heroes} = 0}modattr --silent --charid @{selected|character_id} --mabtempmod|1 --rabtempmod|1 --Bless|1 {&end}   (^)%^( Réjean.ActivateBless) }} EDIT: now I think the ActivateBless ability should start with a bang, and should include a {& simple} -- effectively making it capable of being run on its own. It might take some testing to tweak the final form...
1680307376

Edited 1680327361
Thanks for your thoughts Tim, OK, so basically we do have to keep the ChatSetAttr commands separate from the Chat Menu template stuff. So in that case I'll keep what I have, it's easier to handle. Curious about one thing though, well two, actually. First, why do you need two bangs in the second part of the code? [ !{{!{&if @{selected|Heroes} ... ]? I would have never tried that.... Also, what does (^)%^ do? I csn see that it "calls" an ability, but why not just call the ability the normal way?
1680537598
timmaugh
Pro
API Scripter
The multiple bangs are doing different things... The one at the start initiates a Script Moderator Message. The full formation of: !{{ ...will trigger ZeroFrame to catch the message as a batch of sub-commands it should issue. The second bang (on the first interior line) is because that line should issue a ChatSetAttr call. If ZeroFrame catches a batch command, it looks at each individual line to see if any of them do NOT begin with a bang (ie, they were intended to be standard/simple chat messages): !{{   !scripthandle --arg|value   I want this to hit chat! }} In the above, the first sub-command will be interpreted as a message intended for some mod script to catch. The second command doesn't begin with a bang, so it effectively becomes (for conceptual purposes): !I want this to hit chat!{&simple} If we didn't have a bang on the ChatSetAttr line you are asking about, then the command would be sent to chat as a standard/simple message, and ChatSetAttr would never take action. For the sake of fullness, the bang could have come AFTER the {&if} conditional (right with the modattr handle): {&if @{selected|Heroes} = 0}!modattr --silent ...because ZeroFrame doesn't catch the batch structure until after the metascripts work on the command line (it catches it at "standard" speed, not meta speed). That means that the IF block will either become the ChatSetAttr command line, or it will become a blank line. When the ZeroFrame batching code looks to dispatch the sub-commands contained within the batch, it filters out empty/blank lines. As for what this bit of syntax is doing: (^)%^ ...check out this post ... specifically the Option 2 heading near the end. The reason it *has* to be deferred is because any sub-command line with a double-closing-brace (like a template statement will have) will *end* the ZeroFrame batch closure (much like an attribute that includes a single closing brace will artificially terminate a roll query, if you retrieve the attribute within the roll query syntax). You can have double-closing brackets in the batch closure, but that is the last line that Roll20 will parse as a part of the batch . That means that you *could* include the template statement as the last command line, and then include the ZeroFrame double-closing bracket on the same line . Also, I would still either defer the template command: (^)&^{template:default} {{... ...or I would use the ZeroFrame version of the template command: {&template:default} {{... ...because you don't want the template designation parsed out and attached to the top level message (the one ZeroFrame catches as a batch). You want it attached to the appropriate sub-command line. Either of the above options should accomplish that requirement. The final result might look something like: !{{   !scripthandle --arg|value   {&template:defuault}{{name=Proof of Concept}}{{Result=This will hit chat}} }} Like I said, that should work... I just don't find it visually appealing to have the ZeroFrame closure on the same line as the last command... so that's why I tend to break out template commands to their own attribute/ability and bring them in with a deferred Fetch call. But that's just a preference, not a requirement if we're talking about the last command in the batch.