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

Is the logic for my sheetworker code impossible, or did I just mess up the code?

1709279321
Cc
Pro
Sheet Author
Main issue is with the 4th group of code. I tried doing "const" math with the result of a startRoll that has successes as the result rather than a dice total, but idk if that's actually possible. If anyone has any tips for how I'd go about including the result of a dice roll's successes in a math equation, it'd be much appreciated! <div class="resist"> <button type="action" name="act_resist">Resist!</button></div> <rolltemplate class="sheet-rolltemplate-soak"> <div class="sheet-template-container"> <h2>Final Damage</h2> <div class="sheet-results"> <h2>{{totalabs::rollsoak}}</h2> </div> </div> </rolltemplate> <script type="text/worker"> on("clicked:resist", function() { getAttrs(["drisk","dr","soakflipswitch"], function(values) { let soakflipswitch = parseInt(values.soakflipswitch)||0; let drisk = parseInt(values.drisk)||0; let dr = parseInt(values.dr)||0; let dredresult = drisk - dr; if(dredresult <= 0) { setAttrs({ "driskdr": 1 }); } else { setAttrs({ "driskdr": dredresult }); } setAttrs({ "soakflipswitch": 1 + soakflipswitch }); }); }); </script> <script type="text/worker"> on("change:soakflipswitch", function(info) { getAttrs(["driskdr","absorption","magicsoakradio","softbody","shelltotal"], function(values) { let driskdr = parseInt(values.driskdr)||0; let absorption = parseInt(values.absorption)||0; if(values.magicsoakradio == 0 && values.softbody == 0 && absorption >= 1) { startRoll("&{template:soak} {{name=Soak}} {{rollsoak=[[[[floor(@{shelltotal})]]d6s>5]]}}", (results) => { if(values.shellreroll == "1") { startRoll("&{template:soakreroll} {{name=SoakR1}} {{rollsoakr1=[[1d6>5]]}}", (results) => { }); } const reroll = results.results.rollsoakr1.result; const roll = results.results.rollsoak.result; const total = Math.max(roll + reroll - driskdr); const totalabs = Math.floor(total / absorption) + 1; finishRoll( results.rollId, { rollsoak: totalabs } ); }); } }); }); </script>
1709326068
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
there's a couple issues with the startRoll code you've provided: Your result checking code needs to be either in the callback or using the result of an async call to startRoll (my preferred method). If you're doing conditional rerolls, you really need to use async/await instead of the callback (this is why async/await is my preferred method) Can you give more details on what you are trying to do?
1709858957
Cc
Pro
Sheet Author
The first sheetworker calculates a value for "driskdr", and works fine. I didn't know how async worked, and I needed the second sheetworker to run upon the first sheetworker being run, which is what  "soakflipswitch": 1 + soakflipswitch  is  for. The second sheetworker checks if a change has been made to  soakflipswitch , then rolls  [[[[@{shelltotal}]]d6s>5]] . It does a "reroll" as well if the right condition is met. The results of the main roll and the reroll are added together, then uses these values for the following equations: const total = Math.max(roll + reroll - driskdr); const totalabs = Math.floor(total / absorption) + 1; The result would then be sent to a rolltemplate, which would send the final result to chat. That's the idea for the code, at least. I'm looking into async now, but it looks kinda confusing. Any help you can provide on how to do async code would be greatly appreciated!
1709862993

Edited 1709912399
GiGs
Pro
Sheet Author
API Scripter
First, think of this as a container: <script type="text/worker"> </script> That's your script block, and all sheet workers go inside that. You don't need a separate script block for each worker. Your first sheet worker has multiple setAttrs calls in one worker. Each of those requires a call to Roll20's servers, which means a (relatively) slow internet message. It's best to use just one setAttrs call. A setAttrs call changes values on a character sheet and you can change several attributes at once.  You can store the changes in a variable - I like to use the name output, because it is being output. on("clicked:resist", function() { getAttrs(["drisk","dr","soakflipswitch"], function(values) { let soakflipswitch = parseInt(values.soakflipswitch)||0; let drisk = parseInt(values.drisk)||0; let dr = parseInt(values.dr)||0; let dredresult = drisk - dr; const output = {}; // create the output variable to contain the final values. output.soakflipswitch = 1 + soakflipswitch; if(dredresult <= 0) { output.driskdr = 1; } else { output.driskdr = dredresult; } setAttrs(output); }); }); I would streamline that a bit by using a ternary operator, which is a way of creating simpe if statements in one line: on("clicked:resist", function() { getAttrs(["drisk","dr","soakflipswitch"], function(values) { let soakflipswitch = parseInt(values.soakflipswitch)||0; let drisk = parseInt(values.drisk)||0; let dr = parseInt(values.dr)||0; let dredresult = drisk - dr; const output = {}; // create the output variable to contain the final values. output.soakflipswitch = 1 + soakflipswitch; output.driskdr = (dredresult <= 0) ? 1 : dredresult; setAttrs(output); }); }); For the startRoll, I'm wondering do you need a second setRoll for the reroll. you can include the reroll (including whether there is one) in the initial roll. on("change:soakflipswitch", function(info) { getAttrs(["driskdr","absorption","magicsoakradio","softbody","shelltotal"], function(values) { let driskdr = parseInt(values.driskdr)||0; let absorption = parseInt(values.absorption)||0; if(values.magicsoakradio == 0 && values.softbody == 0 && absorption >= 1) { let rollString = "&{template:soak} {{name=Soak}} {{rollsoak=[[[[floor(@{shelltotal})]]d6s>5]]}}"; if(values.shellreroll == "1") { rollString += "{{rollsoakr1=[[1d6>5]]}}"; } else { // this else branch is just to make the later code easier. In your roll temmplate you'd only display it if it has a value > 0 rollString += "{{rollsoakr1=[[0]]}}"; } startRoll(rollString, results => { const reroll = results.results.rollsoakr1.result; const roll = results.results.rollsoak.result; const total = roll + reroll - driskdr; const totalabs = Math.floor(total / absorption) + 1; finishRoll(results.rollId, { rollsoak: totalabs }); }); } }); }); This part could be condensed with a ternary operator combined with template literal (fancy way to combine strings and code): let rollString = "&{template:soak} {{name=Soak}} {{rollsoak=[[[[floor(@{shelltotal})]]d6s>5]]}}"; if(values.shellreroll == "1") { rollString += {{rollsoakr1=[[1d6>5]]}}; } else { // this else branch is just to make the later code easier. In your roll temmplate you'd only display it if it has a value > 0 rollString += {{rollsoakr1=[[0]]}}; } which could look like this: let shellroll = parseInt(values.shellreroll) || 0; let rollString = `&{template:soak} {{name=Soak}} {{rollsoak=[[[[floor(@{shelltotal})]]d6s>5]]}} {{rollsoakr1=[[${shellroll == 1 ? 1d6>5 : 0}]] }}`;
1709863688

Edited 1709863747
GiGs
Pro
Sheet Author
API Scripter
Earlier, Scott said this: Scott C. said: Your result checking code needs to be either in the callback or using the result of an async call to startRoll (my preferred method). He was referring to this part: if(values.shellreroll == "1") { startRoll("&{template:soakreroll} {{name=SoakR1}} {{rollsoakr1=[[1d6>5]]}}", (results) => { }); } That startRoll calculates a variable ( results ), but that variable vanishes as soon as the startRoll ends (at that }); ). So when you later try to set the reroll value, it is undefined - it doesnt exist. Even if it did exist, you have two different results variables there, and you can only have variable with the same name. The second one would not add to the first - it would overwrite it. The code I gave above solves those problems. You can nest a second startRoll inside a first one, but the code you have isn't the way to do it. But in this case, it shouldn't be necessary.
1709885832

Edited 1709885960
Cc
Pro
Sheet Author
I made it so all the sheetworkers are between a singular container, I remembering you mentioning that in a past post, apologies for not picking up what you meant xD I'm not sure if this is an error, but you wrote for the reroll: {{rollsoakr1=[[${shellroll == 1 ? 1d6>5 : 0}]] }} Using this causes all my sheetworker code to stop working. Changing the $ to @ fixes them, but the "Resist!" button does not work, outputting this error: No attribute was found for @{Rfsgtertfgwse|shellroll == 1 ? 1d6>5 : 0} However, when I change the code to the less streamlined method: let rollString = "&{template:soak} {{name=Soak}} {{rollsoak=[[[[floor(@{shelltotal})]]d6s>5]]}}"; if(values.shellreroll == "1") { rollString += {{rollsoakr1=[[1d6>5]]}}; } else { // this else branch is just to make the later code easier. In your roll temmplate you'd only display it if it has a value > 0 rollString += {{rollsoakr1=[[0]]}}; } there's still an error (note that streamlining the  on("clicked:resist"  part of the code DOES work without issue). The rolltemplate did send text to chat associated with the "soak" template, but no resulting roll was displayed. Is Roll20 capable of doing successes addition, then displaying the result? Or is it some issue with the rolltemplate?
1709900341
GiGs
Pro
Sheet Author
API Scripter
> Is Roll20 capable of doing successes addition, then displaying the result? Or is it some issue with the rolltemplate? Roll20 is capable of doing this (it';s easy for it). It's likely there is a syntax issue in there somehow, probably in my code! I'll look over the code later.
1709912299

Edited 1709912609
GiGs
Pro
Sheet Author
API Scripter
I have found a few problems with the last worker. First, my issue: it needs an extra }); at the end. Other issues: your code includes this: if(values.shellreroll == "1") but the getAttrs does not include that attribute: getAttrs(["driskdr","absorption","magicsoakradio","softbody","shelltotal"], so this will always return no reroll. Finally an error in the rolltemplate. You need to use the computed keyword here, not the name of the variable you created. So that should be &lt;rolltemplate class="sheet-rolltemplate-soak"&gt; &lt;div class="sheet-template-container"&gt; &lt;h2&gt;Final Damage&lt;/h2&gt; &lt;div class="sheet-results"&gt; &lt;h2&gt;{{computed::rollsoak}}&lt;/h2&gt; &lt;/div&gt; &lt;/div&gt; &lt;/rolltemplate&gt; So to repeat, here are the fixes: add an }); to the end of my code (already added to the above code for convenience) Add a reference to the attribute name you are using for rerolls in the getAttrs and if statement Change the rolltemplate to include the computed keyword. I have a section on custom roll parsing on this page: <a href="https://cybersphere.me/roll20-sheet-author-master-list/" rel="nofollow">https://cybersphere.me/roll20-sheet-author-master-list/</a> . CRP is not explained clearly, and I needed to look at that to remind myself why step 3 above wasn't working!
1709919065
Cc
Pro
Sheet Author
It works! Thank you so much for the help! I didn't realize either that calling&nbsp; setAttrs so much was slowing down my code; the rolls with this new code was pretty instant, so I'm gonna have to go back and fix some things up. Thank you again for your help with this GiGs, I deeply appreciate it!
1709921671

Edited 1709921710
Cc
Pro
Sheet Author
I do have one quick question: There's a scenario where the result of a reroll should not be considered if a player got maximum successes on the regular roll. I'm trying to implement it, but running into an issue I assume with trying to compare the successes result to an attribute number. This is the idea for the code: const reroll = (roll != values.shelltotal) ? results.results.rollsoakr1.result : 0; How would I go about comparing the successes of a roll to the number of dice that was rolled?
1709922643

Edited 1709926672
GiGs
Pro
Sheet Author
API Scripter
Cc said: It works! Thank you so much for the help! I didn't realize either that calling&nbsp; setAttrs so much was slowing down my code; the rolls with this new code was pretty instant, so I'm gonna have to go back and fix some things up. Thank you again for your help with this GiGs, I deeply appreciate it! The reason that your code was slow before probably wasn't setAttrs - that can cause a slow down but you usually need a lot of them to see an impact. The issue was probably that extra startRoll, and the fact it didnt have an accompanying finishRoll. This can have a really big effect. You want a finishRoll in there even if its not apparently doing anything (it is, it's terminating the roll: roll20 waits several seconds for it if it's not there). Cc said: I do have one quick question: There's a scenario where the result of a reroll should not be considered if a player got maximum successes on the regular roll. I'm trying to implement it, but running into an issue I assume with trying to compare the successes result to an attribute number. This is the idea for the code: const reroll = (roll != values.shelltotal) ? results.results.rollsoakr1.result : 0; How would I go about comparing the successes of a roll to the number of dice that was rolled? Here you want to compare the successes to the number of rolled dice. personally I'd move values.sheeltotal out of the calculation so it's easier to use things like console.log to interrogate its value and ensure its the data type you want. Then you could have something like: const shelltotal = parseInt(values.shelltotal) || 0; const reroll_calculated = results.results.rollsoakr1.result; const reroll = (roll != shelltotal) ? reroll_calculated : 0; You define reroll elsewhere - make sure you delete that.
1709926232
Cc
Pro
Sheet Author
It works! Thank you so much again, hopefully this is my last question on coding (for today at least xD)