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

GroupCheck and @'s and token attributes

I have a couple of attack roles that include additional properties, such as: To Hit: 2+@{selected|bar3} Reflected in a group check command, it seems that should work with something like this: !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit}] but because that contains an @, this roll is rendered like this: d20+2+ A plus appears at the end right at the point where the @ shows up. To account for that, i can do something like this: !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit} \at{morale}\] ...which works OK... except that changing character sheet attributes like morale changes them for every similar unit. This is why my character sheet rolls reference bar3 normally. Incidentally, this does not work: !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit} \at{bar3}\] Which appears to be because {selected|bar3}, being a token-level attribute, is unsupported. nor does this (though i wouldnt expect it to work like this either): !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit} \at{selected|bar3}\] Is there any way i can escape around @s in my character sheet, and/or coerce groupcheck into looking at token attributes?
.. tl;dr -- i'm trying to run %{selected|repeating_npcaction_$0_npc_action} for every token in a group... Preferably using groupcheck, but since it looks like groupcheck is expecting to make rolls (not just do whatever i want), i'm trying to find some other ways to get the same result.
1715972443
Gauss
Forum Champion
If it is expecting to make rolls why don't you wrap it in inline brackets? Or make a roll of 1d0?
I tried a d0 option earlier with %{selected|repeating_npcaction_$0_npc_action}, but it only ran it for one user. Is there a certain syntax you'd suggest?
1715984643

Edited 1715984681
Gauss
Forum Champion
I did some testing and I don't see any way for it to directly pull from the bars.  I tried pulling from an attribute linked to the bar but due to Roll20 substitution methods it is the same thing as pulling directly from the Bars.  Could you tell me more about what you are trying to do? Is this for NPCs? PCs? Why are you using Bars instead of Attributes?  My suggestion, use attributes and set up buttons to change the attributes as desired using ChatSetAttr.  Alternately, I suggest posting this in the Mods (API Scripts) forum where you will get advice more specific to Mods (API Scripts). 
Shoot! you're right! I put this in the wrong forum :(. Could you tell me more about what you are trying to do? Is this for NPCs? PCs? This is for NPCs. Players move groups of units across a map, for a kind of large scale warfare type thing, none of which are their characters. Each player might have a group of archers, for example, which have a certain amount of HP, and a certain amount of "morale", which is indicated by Bar3 on the token.  Since PCs might both be using Archer units, i can't use the sheet attributes here, even though that'd definitely be the easiest way to do this; changes to morale might end up changing morale for all of the PCs with archer units, as well as all of the npc archer units.
teamgaur said: I tried a d0 option earlier with %{selected|repeating_npcaction_$0_npc_action}, but it only ran it for one user. Is there a certain syntax you'd suggest? If all you want to do is roll the first attack for multiple tokens, I think SelectManager can do this directly. I'm not sure if you'll need to use GroupCheck at all if you're just trying to roll the first repeating attack from multiple tokens. I don't have the specific commands handy, but it'll start with !forselected.  If you can't figure it out then hopefully Timmaugh can jump in and help out.
Thanks for your help with this so far! If all you want to do is roll the first attack for multiple tokens, I think  SelectManager  can do this directly. I'm not sure if you'll need to use GroupCheck at all if you're just trying to roll the first repeating attack from multiple tokens. I played around with this a bit, but wasn't able to get it to work very elegantly. In the end I've got a kind of convoluted way of doing this, where i've got a group-attack macro that runs for all selected tokens using SelectManager. That macro does three things: sets the morale value to equal the value in Bar 3 using chatsetattr -- Yes... kind of a weird, backwards way to do it, but it works. pauses briefly... because oddly, i found that chatsetattr wasnt able to set the morale attribute quick enough before the next roll. !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit} \at{morale}\] Having a group check happen for all selected units seems a little overkill, but weirdly, it doesnt result in a bunch of erroneous rolls or anything weird. This seems like a really roundabout way to do this: !forselected %{selected|repeating_npcaction_$0_npc_action} ... since that already considers advantage with morale (via bar3) and damage rolls with morale which are also scaled based on token HP. 
1716045704

Edited 1716045719
teamgaur said: I played around with this a bit, but wasn't able to get it to work very elegantly. In the end I've got a kind of convoluted way of doing this, where i've got a group-attack macro that runs for all selected tokens using SelectManager. That macro does three things: sets the morale value to equal the value in Bar 3 using chatsetattr -- Yes... kind of a weird, backwards way to do it, but it works. pauses briefly... because oddly, i found that chatsetattr wasnt able to set the morale attribute quick enough before the next roll. !group-check --custom Attack (+Morale), \[d20 + \at{repeating_npcaction_$0_attack_tohit} \at{morale}\] There might be a better way to do what you’re looking for.  Can you describe the mechanics of what you’re trying to perform without how you’re trying to perform it? In other words, how would you describe what would happen if you were playing in person using physical dice and no VTT at all? Also, what game are you playing and what character sheet are you using? A screenshot is helpful if you’re not sure. 
1716046842
timmaugh
Forum Champion
API Scripter
What you're running into with the forselected solution is *when* items resolve. When you send a forselected message, you can think of that like a "parent" message which spawns an individual "child" message for each of the selected tokens. Each of these messages will go through Roll20 parsing (to resolve token property retrieval, as well as attribute, ability, and macro retrieval. The only caveat for these messages is that for a script-generated message (like the parent message spawning a child message), there is no selected token until metascripts get involved, so no Roll20 construction like: @{selected|attribute} ...will resolve. You have to use the fetch version: @(selected|attribute) ...which will only resolve after SelectManager has supplied the appropriate token as the "selected" token. The other thing going on in this command line: !forselected %{selected|repeating_npcaction_$0_npc_action} Is that, as the parent message, this will go through the Roll20 parsers and it WILL resolve the "selected" token to get the correct npc_action from the first selected token. That npc_action, also contains a reference to the selected token (this time the bar3 value), and that gets resolved... from the first token. So by the time forselected dispatches all of the child commands, there is no differentiation for each token. So. We need to use both Fetch constructions AND forselected deferrals to make sure that when we are sending messages for an individual token, we are getting individualized results. If we're going to use a Fetch construction to get the npc_action, we can't rely on a Roll20 construction to get the bar3. We'll have to use a Fetch construction there, too. That means you can either change the "To Hit" field to be: 2+@(selected.bar3) That won't need to be deferred because by the time we retrieve it, we'll already be in each child message. However, it will make it so that you'll see the Fetch construction (rather than the resolved bar3 value) unless you're feeding it to a script command... the metascripts only work in messages that are intended for the Scritp Moderator. A better plan is to remove the bar3 reference completely and just know that you'll always use the command line, below, to get the appropriate value... since the below command line already adds in the bar3 value. So, assuming you've made the "To Hit" field read "2" (for this character), then here is a forselected line that will spit out a new return for each selected token: !forselected(^) {^\&global ([CheckTarget]?{Target|0})}/w gm @^(selected.token_name) rolls a {^&math *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)}. ***{^\&if {^&math 2 + @^(selected.bar3)} > CheckTarget}Success!{^\&else}Failure!{^\&end}***{^&simple}{^&delay .1} That will do it... but... I don't like the output so much: So here's a slightly more complex version that uses ZeroFrame batching and Muler to build template-parts for each token which we then roll up into a single message at the end: !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^\&global ([CheckTarget]?{Target|0})}set^\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)={^&math 2 + @^(selected.bar3)} ***{^\&if {^&math 2 + @^(selected.bar3)} > CheckTarget}Success!{^\&else}Failure!{^\&end}***(})/set{^&simple}{^&delay .1}   (^)/w gm {^&template:default}({)name=Morale Attack VS ?{Target}(})get^.MuleCharacter.GroupCheckResults.Morale/get{^&delay 2} }} That leads to a much cleaner (IMO) output: If you wanted to use a different template, the portion saved to the Mule could be rewritten to include line breaks... then all of the results could appear in a single "field" of the chosen template. If you want to go that way and can't figure it out, share the name of the sheet (from the Game Settings page)  you are using, the template you want to use, and the field of the template where you want this information to appear, and I can try to work up an option in that vein, too.
Can you describe the mechanics of what you’re trying to perform without how you’re trying to perform it? In other words, how would you describe what would happen if you were playing in person using physical dice and no VTT at all? Also, what game are you playing and what character sheet are you using? A screenshot is helpful if you’re not sure.  I'm trying to work through some mass combat mechanics that might not translate to physical dice due to the amount of rolls and some calculations that need to be made (such as damage rolls that are scaled to HP, kind of like a very aggressive version of swarms). Besides for the "Morale" check (which is actually just bar3), it's using the D&D 5E by Roll20 character sheet. Players move groups of NPC tokens across the board, and engage in combat against other NPCs. !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^\&global ([CheckTarget]?{Target|0})}set^\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)={^&math 2 + @^(selected.bar3)} ***{^\&if {^&math 2 + @^(selected.bar3)} > CheckTarget}Success!{^\&else}Failure!{^\&end}***(})/set{^&simple}{^&delay .1}   (^)/w gm {^&template:default}({)name=Morale Attack VS ?{Target}(})get^.MuleCharacter.GroupCheckResults.Morale/get{^&delay 2} }} This doesn't seem to work, but i'm not sure if i'm doing something weird with it. It's setup as an ability on one of the aforementioned NPCs. The template you used actually works pretty well -- but it seems like it's not running using the right token.
1716125768
timmaugh
Forum Champion
API Scripter
Is your sandbox up and running? Do you get any output at all? Also, not sure if it will matter, but you might need to configure SelectManager (you only need to run this command one time): !smconfig +playerid +who That will give it more freedom in constructing the message. If it doesn't work, post back with more details about what is happening and if I can't figure it out from that, maybe you can invite me and I can look at it in your game.
1716134707

Edited 1716137798
Oh interesting -- I've run that now, but i dont see a change in the output.  I think these are missing something though. This one  !forselected(^) {^\&global ([CheckTarget]?{Target|0})}/w gm @^(selected.token_name) rolls a {^&math *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)}. ***{^\&if {^&math *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)} > CheckTarget}Success!{^\&else}Failure!{^\&end}***{^&simple}{^&delay .1} Looks like it's not measuring 1d20+(tohit)+(bar3), but just tohit+bar3, and comparing that against a target, but that number isn't going to change each round unless their bar3 changes. The roll should actually be the former option with the included d20. Is there a way i can add a d20 into the initial "roll", and include that same result in the comparison? Right now, even if the inline math to included a d20 roll, the comparison would also need to include the same roll result in its calculation.  For this one... !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^\&global ([CheckTarget]?{Target|0})}set^\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)={^&math *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)} ***{^\&if {^&math *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)} > CheckTarget}Success!{^\&else}Failure!{^\&end}***(})/set{^&simple}{^&delay .1}   (^)/w gm {^&template:default}({)name=Morale Attack VS ?{Target}(})get^.MuleCharacter.GroupCheckResults.Morale/get{^&delay 2}{^&simple} }} This one seems like it has the same problem, but only the name of the default template is appearing, and  get^.MuleCharacter.GroupCheckResults.Morale/get{^&delay 2}{^&simple} Isn't returning the roll results from earlier. But it might be that i didnt setup the mule correctly.
1716213443
timmaugh
Forum Champion
API Scripter
OK, try this revamped version. This version uses a d20 in the roll, and it will report the individual rolls AS rolls in the final version. How? Metamagic. Seriously, there's a lot of tricks I'm pulling... I'll explain if you really want to understand so you can pull it off in another context, but for now I'll just press on to keep this post short. Regarding setup... 1) obviously the player who wants to run this has to have controlling rights to the NPC tokens so they can choose them 2) The players who want to run this should have controlling rights to the "MuleCharacter"; they don't need to see it in their journals, but they have to be able to write to it 3) ... I think that's it. Here is the command line: !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^&global ([CheckTarget]?{Target|0}) ([ChkRolld20] [^[^1d20^]^].value)} {^\\\&global ([CheckRoll][^\\][^\\]ChkRolld20 + 0*^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\\]^\\])}set^\\\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)=[~[~ChkRolld20 + 0*^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)~]~] {^\\\&if CheckRoll >= CheckTarget}Success!{^\\\&else}Failure!{^\\\&end}(})/\\\\set{^&delay .1}   (^){^&template:default}({)name=Morale Attack VS ?{Target}(}){^&eval}replace(--source|`get^.MuleCharacter.GroupCheckResults.Morale/get` --find|~[~|[ --find|~]~|]){^&/eval}{^&delay 2} }} And resulting output:
1716228280

Edited 1716228523
Thanks Timmaugh, This looks like it's working now, but i think i might be missing a script or something. My output is looking a little weird, rendering tildes in place: I guess that you're trying to find that second bracket and replace it with a standard bracket at the end of this script (find|~[~|[ --find|~]~|]). I'm not familiar with the --find argument though. Am i missing a script?
1716230632
timmaugh
Forum Champion
API Scripter
That should be in Plugger (which is a part of the Metascript Toolbox), and was released in version 1.0.8 of Plugger. If you can't tell from watching your script deck during a sandbox reboot (the versions of each script report, there), you can install ScriptInfo from the 1-click and then run the command: !scriptinfo That should give you a version of all of the scripts that are set up to use a trick that the Aaron developed for the backend. Long and short of it, you should get an output like this, including the version of Plugger: Another way to test it would be to run a simple command to see if the "replace" plug-in works... something like this: !This output should have no ~[~ in the word {&eval}replace(--source|for~[~ation --find|~[~|m){&/eval}.{&simple} That should process out and leave you a message: This output should have no ~[~ in the word formation. If all of that checks out, then we're likely dealing with a matter of timing for the syntax not being detected at the correct time. I'll take a look on my end, again, but let me know if you get the right answers to the above tests.
Plugger was the thing that was missing. This works now, amazing! Thank you! ... For my information, the backslashes you've added in here are kind of throwing me off. Why are some of these noted with 2 or 3 backslashes? I was trying to add in a damage roll after the success by doing an inline call to *^(selected.npcaction.$0.attack_damage) but my output is coming out weird.  In either case, my damage is set to the item listed in parentheses below: Fetch calls ( (ceil((1D8+3)*(@(selected.bar1)/@(selected.bar1_max))))+@(selected.bar3)) : Success! (ceil((1D8+3)*(14/14)))+2 Damage! Normal ( (ceil((1D8+3)*(@{selected|bar1}/@{selected|bar1|max})))+@{selected|bar3} ):  Success! (ceil((1D8)*(selected|bar1/selected|hp|max)))+selected|morale Damage! Based on the above, i'm guessing that the right way to do this is to have the OnHit amount be void of any character references, and then to reference them in the script instead, so switch it out to 1D8+3, and then move the fetch variants into the script, but then i'd need to add an in-line roll after "Success".
1716250306

Edited 1716250347
Never mind! I think i got it. I'm using this now. I couldnt get it to roll damage by calculating the full roll through the sheet, but this actually works pretty well. !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^&global ([CheckTarget]?{Target|0}) ([ChkRolld20] [^[^1d20^]^].value)} {^\\\&global ([CheckRoll][^\\][^\\]ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\\]^\\]) ([rolldmg] [^\\[^\\(ceil((*^(selected.npcaction.$0.attack_damage))*(@^(selected.bar1)/@^(selected.bar1_max))))+@^(selected.bar3)\\]^\\]^.value)}set^\\\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)=[~[~ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)~]~] {^\\\&if CheckRoll > CheckTarget}Success! [^\\\\[^\\\\[rolldmg]]^\\\\]^\\\\ Damage!{^\\\&else}Failure!{^\\\&end}(})/\\\\set{^&delay .1}   (^){^&template:default}({)name=Attack VS ?{Target}(}){^&eval}replace(--source|`get^.MuleCharacter.GroupCheckResults.Morale/get` --find|~[~|[ --find|~]~|]){^&/eval}{^&delay 2} }} Because of the template i'm using here, i can't get the damage roll to show up as a dice roll. I'll play with that a little bit later. I'm going to ask a new, separate question about nested IF statements (and put it into the correct forum this time). Thank you for your help with this!
1716251139

Edited 1716309041
timmaugh
Forum Champion
API Scripter
(I see you've worked a few things out... I started this post earlier, so I'll go ahead and share it anyway in case it helps) First off... awesome. It's working. Second... about those backslashes... I should say before I begin that there is a difference between a backslash and a forward slash. A forward slash (for metascript syntax) simply denotes the end of a construction. So, a Muler set statement might look like: set. ... /set Or a Plugger statement might look like: {&eval} ... {&/eval} Those are separate from the backslashes. So, to understand those... The trick that (I think) keeps more people from using the metascripts more is understanding the timing of them. Metascripts happen after initial Roll20 parsing (roll queries, inline rolls, attribute retrieval, etc.), but  before  other/standard scripts. (For a discussion of this, go to this  Roll20 Discord  link, where I explained things in more detail.) ZeroFrame, one of the metascripts in the toolbox, organizes the others into an order that works for 99% of messages (and it allows for a way to change the order, as necessary). SelectManager runs before Fetch... APILogic is the last to run... you can see the order if you run: !0 At the end of each script having a chance at the message, ZeroFrame detects if anything was changed -- if any metascript did any work, like Fetch returning something from a character. If something was changed, then  new  metascript constructions might have been introduced... a muled variable might have been added to the command line that also included a Fetch statement, so we need to give Fetch a chance to resolve that construction. That's why, if ZeroFrame detects that anything was changed, it will run the series of scripts again. It first exposes the command line to the Roll20 parsers (resolving inline rolls, character attribute references, etc. -- everything but @{select} references and roll queries), then it starts down the series of metascripts, again. We call this the "loop". Since some formations won't be able to resolve until we've gone a loop or two deep in our processing, ZeroFrame offers a way to "hide" text constructions: the backslash. At the end of each loop where work was done to the message, a backslash is removed and the loop is run again. That makes it so that if we need to use Fetch to reference some math that we do in the first loop (and store it somewhere), we can hide the Fetch construction using a backslash:  @\(selected.hp) A single backslash represents one loop cycle of deferring.  We need as many backslashes as we have loops before that piece of information is available. An example: Inline rolls happen before metascripts, so if we need to retrieve a piece of information that isn't available through normal Roll20 syntax (maybe a token's "top" property), we have to get it with a Fetch construction. That means that the inline roll has to be deferred so that it doesn't run with the initial Roll20 parsing, but is only discovered after the first loop. If we then need to use the result of that roll in a separate Plugger operation, that operation   has to wait until the roll is available, so it has to also be deferred for that first loop. If something else is dependent on the Plugger operation (and it would occur *later* in the order of metascripts), it will have to be deferred until after the second loop... etc. Make sense so far? Because it gets a little more complex before we're done. With a message like forselected (which dispatches a new command for each selected token) or a ZeroFrame batch statement (a bunch of commands batched up between braces: !{{   ...command 1...   ...command 2...   ...etc... }} ...), we have a single, top level message (call it the parent message) followed by multiple secondary messages (call them children messages). In fact, if you have a forselected command in a ZeroFrame batch (like we did for you), you actually have a "grandparent" message (the original batch set message), followed by a parent message (the message that ZeroFrame dispatches and is picked up by forselected), followed by children messages (the messages dispatched by forselected for each selected token). Each one of these messages gets its own metascript loop series... looping until ZeroFrame detects that no further meta-work was done to them. Therefore, if a metascript construction is present in the command line of the parent message (or grandparent message, in your case), it will pass through the loops of the parent message's metascript cycle... which means that it will also go through the undeferring (the removing of backslashes at the end of every loop). So if, as an example, we know that an embedded command in a batch set has to wait until the second loop  of its own cycle  to resolve, but we also know that the parent message has some meta-work going on, then the parent loop will already trigger one deferral. That means that the formations that need to be deferred in the embedded command will need 1 deferral for each of the parent message loops, and another 1 deferral for its own loop. You can see this in your command line, right at the top. !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set Your command is a batch, where the first command (the first dispatched message) is a Muler set statement (to reset the variable we will eventually report out).  That means that we're doing work in the top level message... so we have to pay attention to things that are a part of other dispatched messages which need to happen in a specific loop of *that* message. You can see this in the forselected line, which comes next in the batch. Here is just the beginning of the line, where we establish a pair of variables: !forselected(^) {^&global ([CheckTarget]?{Target|0}) ([ChkRolld20] [^[^1d20^]^].value)} {^\\\&global ([CheckRoll][^\\][^\\]ChkRolld20 + 0*^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\\]^\\])} When the full message goes through the metascript loop, one loop is required to finish the metascript constructions in the first line. That means that after that loop finishes (and ZeroFrame can get around to dispatching the forselected line), it will have removed one backslash from every series of backslashes. The forselected line excerpt will now look like this (note I am going to substitute a "10" as the target number, since the query would have resolved by this point): !forselected(^) {^&global ([CheckTarget]10) ([ChkRolld20] [^[^1d20^]^].value)} {^\\&global ([CheckRoll][^\][^\]ChkRolld20 + 0*^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\]^\])} Now, one more thing to realize: forselected offers a way to declare its OWN deferral character, within the parentheses that follow the script handle. I've used a caret because I know I don't need that character for the rest of the line that will be dispatched for each token. So when forselected dispatches this command line for each token, those carets will be removed, and the line that will be sent for each token will start like this: {&global ([CheckTarget]10) ([ChkRolld20] [[1d20]].value)} {\\&global ([CheckRoll][\][\]ChkRolld20 + 0*(selected.npcaction.$0.attack_tohit) + @(selected.bar3)\]\])} You can see how that now exposes Fetch constructions (the  @(selected.bar3)  reference), as well as the {&global... } variable declaration. That means, when each of these messages hit their own metascript loop cycle, there will be work done in the initial pass, so a new loop will be triggered. That will remove another backslash (and, for inline rolls that are at a single backslash, it will replace [\] with [ ), exposing inline rolls. The above snippet will now read (where the 1d20 resulted in an 8 and was substituted in for the ChkRolld20 variable, the tohit number was a 2, and the bar3 value was 12): {\&global ([CheckRoll][[8 + 02 + 12]])} You can see that we've no exposed an inline roll, which will get caught by the Roll20 parsers as the next loop begins. Coupled with removing the last backslash, and we have a {&global} variable declaration that will be detected (no more deferrals breaking up the text pattern), and it will be assigned to the result of the roll. Now you can take that to the rest of the roll... where you see anything that was waiting on the CheckRoll global variable, that construction will have to have at least as many deferrals as what the {&global} tag started with. Since global-variable replacements happen at the top of the loop, everything else in the metascript loop comes after them and shouldn't have to wait for them (maybe something else, but not them, anymore). That's what you need to understand about the backslashes, but as long as we're on the topic of deferrals, I should mention that ZeroFrame batching construction comes with 2 potential places to declare deferral text (since a ZeroFrame batch is going to dispatch child messages, you might need to preserve a construction until after the parent-message processing has finished, same as for a forselected message). The syntax for batch deferrals is the same as it is for forselected (enclosing it in parentheses). You can declare it for the entirety of the batched set of commands by following the opening braces: !{{ (^)   ... }} And/or you can declare deferral characters for an individual line by including it as the first text on that line, even before an exclamation point if the line would begin with one: !{{   (~)!token-mod --set ... }} You can see one of these line-deferral declarations on the last line in the batch, where it launches the template: !{{   ...    (^){^&template:default}({)name=... }} Anyway... that's the explanation. Hit me up if you have any more questions!
1716253823
timmaugh
Forum Champion
API Scripter
teamgaur said: Never mind! I think i got it. I'm using this now. I couldnt get it to roll damage by calculating the full roll through the sheet, but this actually works pretty well. !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^&global ([CheckTarget]?{Target|0}) ([ChkRolld20] [^[^1d20^]^].value)} {^\\\&global ([CheckRoll][^\\][^\\]ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\\]^\\]) ([rolldmg] [^\\[^\\(ceil((*^(selected.npcaction.$0.attack_damage))*(@^(selected.bar1)/@^(selected.bar1_max))))+@^(selected.bar3)\\]^\\]^.value)}set^\\\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)=[~[~ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)~]~] {^\\\&if CheckRoll > CheckTarget}Success! [^\\\\[^\\\\[rolldmg]]^\\\\]^\\\\ Damage!{^\\\&else}Failure!{^\\\&end}(})/\\\\set{^&delay .1}   (^){^&template:default}({)name=Attack VS ?{Target}(}){^&eval}replace(--source|`get^.MuleCharacter.GroupCheckResults.Morale/get` --find|~[~|[ --find|~]~|]){^&/eval}{^&delay 2} }} Because of the template i'm using here, i can't get the damage roll to show up as a dice roll. I'll play with that a little bit later. I'm going to ask a new, separate question about nested IF statements (and put it into the correct forum this time). Thank you for your help with this! OK, so you're running into the last part that tripped me up. I'll tell you how I got around it, then you can see if you can duplicate it... Our forselected line is appending the portion of our command line for each token that matters... basically writing to the mule the text between the set and /set , whatever the state of that text is. If you've read the previous post, you know that in each loop the metascripts run after the Roll20 parsers have a look at the line, resolving rolls down to roll markers... ie, $[[0]] That roll marker is what is in the command line when we write the template information for each token to the mule... so you are basically telling it to write something like: {{ Character= ... Success! $[[0]] Damage!}} The data behind that roll is lost by the time we run the last line in our batch to retrieve all of these template parts to construct our output message. We no longer have the roll information to attach to that part of the message to construct the display with the roll tip. I see you went with the trick where you include an extra set of brackets to trick Roll20 into unpacking the roll. That works because the roll is unpacked during the Roll20 parsing of the line just prior to your set statement writing it to the mule. But if you want to preserve the roll, you can see what I did for the "check" (the part that is already reporting as a roll). For that one, I thought about what I wanted the equation of the roll to be when the final output hit the chat. When you hover over those rolls, you see the d20 result plus the tohit number plus the current token's bar3 value. The roll shows you the total, but it's nice to be able to hover to see what the d20 actually was. What I needed was for THIS roll (in the output panel) to resolve "just in time" as you run it. That's where the Plugger code comes in... When I write to the mule, I write the roll that I want to show up as: [~[~ ...roll equation here... ~]~] That is enough that it will not be detected by Roll20 parsers as any sort of roll -- the tildes will always break up the formation. Then, in the last command, I retrieve the attack result from the mule (with the get statement) and feed it to the Plugger replace function as my "source". I do 2 quick replaces on the text to replace ~[~ with [ and ~]~ with ]... leaving me with a new, clean, inline roll. We're in the loop of the last child message, and we've just performed meta-work, so ZeroFrame will give us another loop at the end, meaning Roll20 will now, IN THE CURRENT MESSAGE which is about to hit the chat, detect the inline roll and parse it, leaving behind the roll data in the message for us to display. The bottom line is for your roll damage, you have to think about what you want that roll equation to look like when you hover over it. If you only want it to be a roll for the purposes of visual similarity (you don't need to see the math), then you can write the result of the roll between [~[~ and ~]~] and let the same Plugger replace function do the work, at run time, of replacing that text. But, as I read what you've got for the roll damage, you don't actually need it anywhere else... it's just math with terms that Fetch is retrieving for you. Your basic roll damage formula is: ceil((*^(selected.npcaction.$0.attack_damage))*(@^(selected.bar1)/@^(selected.bar1_max))))+@^(selected.bar3) You put that in a roll, extract the value to assign it to a variable... then you later put that variable back in a roll to report the damage. Let's simplify that. All of those Fetch constructions will be resolved during the first loop of the message dispatched for each token... so, like you've done, you only need the forselected deferral character (^) to make sure they survive that long. Good.  After that, we don't actually care about using this computed number in any further calculations -- we only need to report it at the end. So why don't we just have the formula, itself, and write it to the mule between [~[~ and ~]~] ? That way, the math is done at run time, and you get another formatted roll in your output. There is one caveat to this approach... and that is if we try to preserve this formula to a global variable, we'll run into a syntax problem. A global variable currently cannot include a closing parentheses. I know. It's a horrible oversight to leave that gap in functionality, but I just haven't gotten around to fixing it. You side-stepped this problem completely by deferring the {&global} tag enough times that by the time it was detected you already had a roll marker (a resolved inline roll). If we remove the inline roll brackets, we lose that protection. That means, when we look at your formula, above, we'd have Fetch tags with closing parentheses, and we have a ceil() operation with a closing parentheses. We already said the Fetch constructions will process out during the first loop they are detected, so we can defer the {&global} tag one time to give Fetch the time it needs. But we'd still have the parentheses associated with the ceil() operation, and that we're going to need at the very end. So we can't do this in a global variable. We need to do it right in the results section... in the TRUE side of the IF block. The good news is that it should get rid of a lot of backslashes, since we no longer need to defer anything. Here's an attempt at aircoding it... removing your global variable for the roll damage, and turning it into a roll that will render during the output... I think. Also, I think you had an extra parentheses in your equation, so I consolidated it... but you can see where it is in the command (below), so you can change it if necessary: !{{   !set.MuleCharacter.GroupCheckResults.Morale = 0/set   !forselected(^) {^&global ([CheckTarget]?{Target|0}) ([ChkRolld20] [^[^1d20^]^].value)} {^\\\&global ([CheckRoll][^\\][^\\]ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)^\\]^\\])}set^\\\\.MuleCharacter.GroupCheckResults.Morale = get^.MuleCharacter.GroupCheckResults.Morale/get({)@^(selected.token_name)=[~[~ChkRolld20 + *^(selected.npcaction.$0.attack_tohit) + @^(selected.bar3)~]~] {^\\\&if CheckRoll > CheckTarget}Success! [~[~ceil(*^(selected.npcaction.$0.attack_damage)*@^(selected.bar1)/@^(selected.bar1_max))+@^(selected.bar3)~]~] Damage!{^\\\&else}Failure!{^\\\&end}(})/\\\\set{^&delay .1}   (^){^&template:default}({)name=Attack VS ?{Target}(}){^&eval}replace(--source|`get^.MuleCharacter.GroupCheckResults.Morale/get` --find|~[~|[ --find|~]~|]){^&/eval}{^&delay 2} }}