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

Automatically Checking and Un-checking Checkboxes After Pressing Buttons to Spend or Replenish a Resource...

In my game The Shades of Dominion, the players are Shade Mages that drain The Shade from the realm into their Shade Vessels, which they can then use to cast spells.&nbsp; &nbsp;Each Shade Vessel has a "Syp Level" which determines how full it is and how difficult it would be to get Shade from it at a moment's notice. The first roll/button, is the Stain Button circled in Red.&nbsp; In addition to all of the things it does with The Shade, I also need it to Uncheck the first left-most Checked box in the Syp Level bar.&nbsp; &nbsp;I just need it to subtract one Syp Level.&nbsp; That's it. The second roll/button is the Syphon Button circled in Green.&nbsp; This button is pressed when the Player wants to fill their Shade Vessel with Shade from the realm.&nbsp; The roll is always a Xd6&gt;4 roll, with usually 2 or more successes.&nbsp; I then need this roll to Check as many boxes, from right-to-left, as there are successes or until the Syp Level reaches "full". The third roll/button is the Syp Check, highlighted in yellow.&nbsp; The purpose of this button is to determine whether or not the Shade Mage was able to concentrate hard enough to extract Shade from their Shade Vessel in high-stress situations (like combat or in the middle of disabling a trap, ect). When pressed it needs to read what the current Syp Level is and roll a single d6.&nbsp; If the result succeeds and is equal to or higher than the current Syp Level, the roll continues as if the Stain Button had been pressed, generating Shade and reducing the Syp Level by one.&nbsp; If the result is a failure, no Shade is generated and the Syp Level remains the same. The point is to not require the player to make any changes the Syp Level Bar themselves, but to have the game track that on its own. I have not started any Sheet Worker or CSS for the Syp Level Bar on any of the above buttons yet...I will provide below what I have done for each button and the HTML of the Syp Level Bar. Syp Level Bar: &lt;label&gt;Syp Level:&lt;/label&gt;&lt;span&gt; Full: &lt;input type="checkbox" class="syplevel" name="attr_syplevelfull" value="6" style="pointer-events: none;"&gt; 2+: &lt;input type="checkbox" class="syplevel" name="attr_syplevel2+" value="5" style="pointer-events: none;"&gt; 3+: &lt;input type="checkbox" class="syplevel" name="attr_syplevel3+" value="4" style="pointer-events: none;"&gt; 4+: &lt;input type="checkbox" class="syplevel" name="attr_syplevel4+" value="3" style="pointer-events: none;"&gt; 5+: &lt;input type="checkbox" class="syplevel" name="attr_syplevel5+" value="2" style="pointer-events: none;"&gt; 6+: &lt;input type="checkbox" class="syplevel" name="attr_syplevel6+" value="1" style="pointer-events: none;"&gt; Empty: &lt;input type="checkbox" class="syplevel" name="attr_syplevelempty" value="0" style="pointer-events: none;"&gt;&lt;/span&gt; Syphon (HTML): &lt;button type="roll" value="&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}}"&gt;Syphon&lt;/button&gt; &lt;rolltemplate class="sheet-rolltemplate-syphonroll"&gt; &lt;div class="sheet-container" &gt; &lt;div class="sheet-header"&gt; {{#title}}&lt;div class="sheet-title"&gt;{{title}}&lt;br&gt;&lt;/div&gt;{{/title}} {{#subtitle}}&lt;div class="sheet-subtitle"&gt;&lt;br&gt;&lt;br&gt;{{subtitle}}&lt;/div&gt;{{/subtitle}} &lt;/div&gt; &lt;div class="sheet-content"&gt; {{#allprops() title subtitle desc color}} &lt;div class="sheet-key"&gt;{{key}}&lt;/div&gt; &lt;div class="sheet-value"&gt;{{value}}&lt;/div&gt; {{/allprops() title subtitle desc color}} &lt;/div&gt; &lt;/div&gt; &lt;/rolltemplate&gt; Stain (HTML): &lt;button type="action" name="act_staincheck"&gt;Stain&lt;/button&gt; &lt;rolltemplate class="sheet-rolltemplate-staincheck"&gt; &lt;div class="sheet-header"&gt; {{#name}}&lt;div class="sheet-name"&gt;{{name}}&lt;/div&gt;{{/name}} &lt;/div&gt; &lt;table&gt; &lt;tbody&gt; &lt;div class="sheet-template-content"&gt; &lt;tr class="sheet-aether_row"&gt; {{#Aether}} &lt;td&gt;&lt;div class="sheet-aether-roll-icon"&gt;&lt;img class="sheet-aether-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716976/LoT1TR39hDI8vQiFTd34Kg/thumb.png?1712432342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716976/LoT1TR39hDI8vQiFTd34Kg/thumb.png?1712432342</a>" alt="Aether Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-aether-roll-title"&gt;Aether&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-aether-roll-result"&gt;{{Aether}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Aether}} &lt;/tr&gt; &lt;tr class="sheet-corruption_row"&gt; {{#Corruption}} &lt;td&gt;&lt;div class="sheet-corruption-roll-icon"&gt;&lt;img class="sheet-corruption-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716826/vQzXciBjpL8mmM4d6zwu7g/thumb.png?1712432290" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716826/vQzXciBjpL8mmM4d6zwu7g/thumb.png?1712432290</a>" alt="Corruption Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-corruption-roll-title"&gt;Corruption&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-corruption-roll-result"&gt;{{Corruption}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Corruption}} &lt;/tr&gt; &lt;tr class="sheet-destruction_row"&gt; {{#Destruction}} &lt;td&gt;&lt;div class="sheet-destruction-roll-icon"&gt;&lt;img class="sheet-destruction-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716891/GreHB-W2DFGcHqwUrmfqSw/thumb.png?1712432316" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716891/GreHB-W2DFGcHqwUrmfqSw/thumb.png?1712432316</a>" alt="Destruction Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-destruction-roll-title"&gt;Destruction&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-destruction-roll-result"&gt;{{Destruction}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Destruction}} &lt;/tr&gt; &lt;tr class="sheet-purity_row"&gt; {{#Purity}} &lt;td&gt;&lt;div class="sheet-purity-roll-icon"&gt;&lt;img class="sheet-purity-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716865/pJTQKWHxPghesYG2oRv7Sw/thumb.png?1712432306" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716865/pJTQKWHxPghesYG2oRv7Sw/thumb.png?1712432306</a>" alt="Purity Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-purity-roll-title"&gt;Purity&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-purity-roll-result"&gt;{{Purity}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Purity}} &lt;/tr&gt; &lt;tr class="sheet-vitality_row"&gt; {{#Vitality}} &lt;td&gt;&lt;div class="sheet-vitality-roll-icon"&gt;&lt;img class="sheet-vitality-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716923/J3FOFhYEFRuRveVN9_nWTg/thumb.png?1712432328" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716923/J3FOFhYEFRuRveVN9_nWTg/thumb.png?1712432328</a>" alt="Vitality Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-vitality-roll-title"&gt;Vitality&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-vitality-roll-result"&gt;{{Vitality}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Vitality}} &lt;/tr&gt; &lt;tr class="sheet-unstained_row"&gt; {{#Unstained}} &lt;td&gt;&lt;div class="sheet-unstained-roll-icon"&gt;&lt;img class="sheet-unstained-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787</a>" alt="Unstained Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-unstained-roll-title"&gt;Unstained&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-unstained-roll-result"&gt;{{computed::Unstained}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Unstained}} &lt;/tr&gt; &lt;/div&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/rolltemplate&gt; Stain (Sheet Worker): on('clicked:staincheck', event =&gt; { const stain_string = "&amp;{template:staincheck} {{name=Stain Check}} {{Aether=[[@{aetherscore}d6&gt;@{aetherstain}]]}} {{Corruption=[[@{corruptionscore}d6&gt;@{corruptionstain}]]}} {{Destruction=[[@{destructionscore}d6&gt;@{destructionstain}]]}} {{Purity=[[@{purityscore}d6&gt;@{puritystain}]]}} {{Vitality=[[@{vitalityscore}d6&gt;@{vitalitystain}]]}} {{Unstained=[[0]]}}"; startRoll(stain_string, roll =&gt; { console.log(roll); let aether_dice = parseInt(roll.results.Aether.dice.length)||0; let aether_results = parseInt(roll.results.Aether.result)||0; let corruption_dice = parseInt(roll.results.Corruption.dice.length)||0; let corruption_results = parseInt(roll.results.Corruption.result)||0; let destruction_dice = parseInt(roll.results.Destruction.dice.length)||0; let destruction_results = parseInt(roll.results.Destruction.result)||0; let purity_dice = parseInt(roll.results.Purity.dice.length)||0; let purity_results = parseInt(roll.results.Purity.result)||0; let vitality_dice = parseInt(roll.results.Vitality.dice.length)||0; let vitality_results = parseInt(roll.results.Vitality.result)||0; let unstained_results = parseInt(roll.results.Unstained.result)||0; let dice_total = aether_dice + corruption_dice + destruction_dice + purity_dice + vitality_dice; let result_total = aether_results + corruption_results + destruction_results + purity_results + vitality_results; let new_unstained = dice_total - result_total; roll.results.Unstained.result = new_unstained; roll.results.Unstained.expression = new_unstained; finishRoll(roll.rollId, { Unstained: new_unstained }); }); }); Any suggestions and pointers are appreciated!
1713127499
GiGs
Pro
Sheet Author
API Scripter
There's a lot to read and comprehend there. It looks like you're asking for help with 3 buttons that each do different things.. I suggest you break it down into one prblem per thread.
Okay, let's start with Button 1: In my game The Shades of Dominion, the players are Shade Mages that drain The Shade from the realm into their Shade Vessels, which they can then use to cast spells.&nbsp; &nbsp;Each Shade Vessel has a "Syp Level" which determines how full it is and how difficult it would be to get Shade from it at a moment's notice. The first roll/button, is the Stain Button circled in Red.&nbsp; In addition to all of the things it does with The Shade, I also need it to Uncheck the first left-most Checked box in the Syp Level bar.&nbsp; &nbsp;I just need it to subtract one Syp Level.&nbsp; That's it. Syp Level Bar: &lt;label&gt;Syp Level:&lt;/label&gt;&lt;span&gt; Full: &lt;input type="checkbox" class="syplevel" name="attr_syplevelfull" value="6" style="pointer-events: none;"&gt; 2+: &lt;inpu Stain (HTML): &lt;button type="action" name="act_staincheck"&gt;Stain&lt;/button&gt; &lt;rolltemplate class="sheet-rolltemplate-staincheck"&gt; &lt;div class="sheet-header"&gt; {{#name}}&lt;div class="sheet-name"&gt;{{name}}&lt;/div&gt;{{/name}} &lt;/div&gt; &lt;table&gt; &lt;tbody&gt; &lt;div class="sheet-template-content"&gt; &lt;tr class="sheet-aether_row"&gt; {{#Aether}} &lt;td&gt;&lt;div class="sheet-aether-roll-icon"&gt;&lt;img class="sheet-aether-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716976/LoT1TR39hDI8vQiFTd34Kg/thumb.png?1712432342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716976/LoT1TR39hDI8vQiFTd34Kg/thumb.png?1712432342</a>" alt="Aether Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-aether-roll-title"&gt;Aether&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-aether-roll-result"&gt;{{Aether}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Aether}} &lt;/tr&gt; &lt;tr class="sheet-corruption_row"&gt; {{#Corruption}} &lt;td&gt;&lt;div class="sheet-corruption-roll-icon"&gt;&lt;img class="sheet-corruption-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716826/vQzXciBjpL8mmM4d6zwu7g/thumb.png?1712432290" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716826/vQzXciBjpL8mmM4d6zwu7g/thumb.png?1712432290</a>" alt="Corruption Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-corruption-roll-title"&gt;Corruption&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-corruption-roll-result"&gt;{{Corruption}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Corruption}} &lt;/tr&gt; &lt;tr class="sheet-destruction_row"&gt; {{#Destruction}} &lt;td&gt;&lt;div class="sheet-destruction-roll-icon"&gt;&lt;img class="sheet-destruction-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716891/GreHB-W2DFGcHqwUrmfqSw/thumb.png?1712432316" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716891/GreHB-W2DFGcHqwUrmfqSw/thumb.png?1712432316</a>" alt="Destruction Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-destruction-roll-title"&gt;Destruction&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-destruction-roll-result"&gt;{{Destruction}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Destruction}} &lt;/tr&gt; &lt;tr class="sheet-purity_row"&gt; {{#Purity}} &lt;td&gt;&lt;div class="sheet-purity-roll-icon"&gt;&lt;img class="sheet-purity-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716865/pJTQKWHxPghesYG2oRv7Sw/thumb.png?1712432306" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716865/pJTQKWHxPghesYG2oRv7Sw/thumb.png?1712432306</a>" alt="Purity Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-purity-roll-title"&gt;Purity&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-purity-roll-result"&gt;{{Purity}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Purity}} &lt;/tr&gt; &lt;tr class="sheet-vitality_row"&gt; {{#Vitality}} &lt;td&gt;&lt;div class="sheet-vitality-roll-icon"&gt;&lt;img class="sheet-vitality-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387716923/J3FOFhYEFRuRveVN9_nWTg/thumb.png?1712432328" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387716923/J3FOFhYEFRuRveVN9_nWTg/thumb.png?1712432328</a>" alt="Vitality Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-vitality-roll-title"&gt;Vitality&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-vitality-roll-result"&gt;{{Vitality}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Vitality}} &lt;/tr&gt; &lt;tr class="sheet-unstained_row"&gt; {{#Unstained}} &lt;td&gt;&lt;div class="sheet-unstained-roll-icon"&gt;&lt;img class="sheet-unstained-icon" src="<a href="https://s3.amazonaws.com/files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787</a>" alt="Unstained Icon Logo"&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-unstained-roll-title"&gt;Unstained&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div class="sheet-unstained-roll-result"&gt;{{computed::Unstained}}&lt;br&gt;&lt;/div&gt;&lt;/td&gt; {{/Unstained}} &lt;/tr&gt; &lt;/div&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/rolltemplate&gt; Stain (Sheet Worker): on('clicked:staincheck', event =&gt; { const stain_string = "&amp;{template:staincheck} {{name=Stain Check}} {{Aether=[[@{aetherscore}d6&gt;@{aetherstain}]]}} {{Corruption=[[@{corruptionscore}d6&gt;@{corruptionstain}]]}} {{Destruction=[[@{destructionscore}d6&gt;@{destructionstain}]]}} {{Purity=[[@{purityscore}d6&gt;@{puritystain}]]}} {{Vitality=[[@{vitalityscore}d6&gt;@{vitalitystain}]]}} {{Unstained=[[0]]}}"; startRoll(stain_string, roll =&gt; { console.log(roll); let aether_dice = parseInt(roll.results.Aether.dice.length)||0; let aether_results = parseInt(roll.results.Aether.result)||0; let corruption_dice = parseInt(roll.results.Corruption.dice.length)||0; let corruption_results = parseInt(roll.results.Corruption.result)||0; let destruction_dice = parseInt(roll.results.Destruction.dice.length)||0; let destruction_results = parseInt(roll.results.Destruction.result)||0; let purity_dice = parseInt(roll.results.Purity.dice.length)||0; let purity_results = parseInt(roll.results.Purity.result)||0; let vitality_dice = parseInt(roll.results.Vitality.dice.length)||0; let vitality_results = parseInt(roll.results.Vitality.result)||0; let unstained_results = parseInt(roll.results.Unstained.result)||0; let dice_total = aether_dice + corruption_dice + destruction_dice + purity_dice + vitality_dice; let result_total = aether_results + corruption_results + destruction_results + purity_results + vitality_results; let new_unstained = dice_total - result_total; roll.results.Unstained.result = new_unstained; roll.results.Unstained.expression = new_unstained; finishRoll(roll.rollId, { Unstained: new_unstained }); }); });
1713146508
GiGs
Pro
Sheet Author
API Scripter
For clarification: when that staincheck button is clicked, you want to run the CRP example above, and reduce the syplevelfull attribute by 1?
Yes.
1713147835
GiGs
Pro
Sheet Author
API Scripter
That's a fairly minor change. You ned to add a getAttrs function so you can get the current value, and a setAttrs to update it. There are multiple places you could put it. You could have them separate from statRoll so that they run independently. &nbsp; &nbsp; on ( 'clicked:staincheck' , event =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const stain_string = "&amp;{template:staincheck} {{name=Stain Check}} {{Aether=[[@{aetherscore}d6&gt;@{aetherstain}]]}} {{Corruption=[[@{corruptionscore}d6&gt;@{corruptionstain}]]}} {{Destruction=[[@{destructionscore}d6&gt;@{destructionstain}]]}} {{Purity=[[@{purityscore}d6&gt;@{puritystain}]]}} {{Vitality=[[@{vitalityscore}d6&gt;@{vitalitystain}]]}} {{Unstained=[[0]]}}" ; &nbsp; &nbsp; &nbsp; &nbsp; startRoll ( stain_string , roll =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console . log ( roll ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let aether_dice = parseInt ( roll . results . Aether . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let aether_results = parseInt ( roll . results . Aether . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let corruption_dice = parseInt ( roll . results . Corruption . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let corruption_results = parseInt ( roll . results . Corruption . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let destruction_dice = parseInt ( roll . results . Destruction . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let destruction_results = parseInt ( roll . results . Destruction . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let purity_dice = parseInt ( roll . results . Purity . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let purity_results = parseInt ( roll . results . Purity . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let vitality_dice = parseInt ( roll . results . Vitality . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let vitality_results = parseInt ( roll . results . Vitality . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let unstained_results = parseInt ( roll . results . Unstained . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let dice_total = aether_dice + corruption_dice + destruction_dice + purity_dice + vitality_dice ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let result_total = aether_results + corruption_results + destruction_results + purity_results + vitality_results ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let new_unstained = dice_total - result_total ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roll . results . Unstained . result = new_unstained ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roll . results . Unstained . expression = new_unstained ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; finishRoll ( roll . rollId , { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Unstained : new_unstained &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ([ 'syplevelfull' ], values =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const syp = parseInt ( values . syplevelfull ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const syp_final = Math . max ( 0 , syp - 1 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; syplevelfull : syp_final &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }) &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; }); You might want to be more restructive, so the roll is only made if there is at least 1 point of syplevelfull to pay for it.That could look like this: on ( 'clicked:staincheck' , event =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ([ 'syplevelfull' ], values =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const syp = parseInt ( values . syplevelfull ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ( svp &gt; 0 ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const syp_final = Math . max ( 0 , syp - 1 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; syplevelfull : syp_final &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const stain_string = "&amp;{template:staincheck} {{name=Stain Check}} {{Aether=[[@{aetherscore}d6&gt;@{aetherstain}]]}} {{Corruption=[[@{corruptionscore}d6&gt;@{corruptionstain}]]}} {{Destruction=[[@{destructionscore}d6&gt;@{destructionstain}]]}} {{Purity=[[@{purityscore}d6&gt;@{puritystain}]]}} {{Vitality=[[@{vitalityscore}d6&gt;@{vitalitystain}]]}} {{Unstained=[[0]]}}" ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; startRoll ( stain_string , roll =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console . log ( roll ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let aether_dice = parseInt ( roll . results . Aether . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let aether_results = parseInt ( roll . results . Aether . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let corruption_dice = parseInt ( roll . results . Corruption . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let corruption_results = parseInt ( roll . results . Corruption . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let destruction_dice = parseInt ( roll . results . Destruction . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let destruction_results = parseInt ( roll . results . Destruction . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let purity_dice = parseInt ( roll . results . Purity . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let purity_results = parseInt ( roll . results . Purity . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let vitality_dice = parseInt ( roll . results . Vitality . dice . length ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let vitality_results = parseInt ( roll . results . Vitality . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let unstained_results = parseInt ( roll . results . Unstained . result ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let dice_total = aether_dice + corruption_dice + destruction_dice + purity_dice + vitality_dice ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let result_total = aether_results + corruption_results + destruction_results + purity_results + vitality_results ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let new_unstained = dice_total - result_total ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roll . results . Unstained . result = new_unstained ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roll . results . Unstained . expression = new_unstained ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; finishRoll ( roll . rollId , { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Unstained : new_unstained &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const stain_string = "No Syp Left; you cannot run this." ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; startRoll ( stain_string , roll =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; finishRoll ( roll . rollId , {}); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; });
I had to add a new line to the rolltemplate, but it works ! Sheet Worker: on('clicked:staincheck', event =&gt; { getAttrs(['syplevel'], values =&gt; { const syp = parseInt(values.syplevel) || 0; if (syp &gt; 0) { const syp_final = Math.max(0, syp -1); setAttrs({ syplevel: syp_final }); const stain_string = "&amp;{template:staincheck} {{name=Stain Check}} {{Aether=[[@{aetherscore}d6&gt;@{aetherstain}]]}} {{Corruption=[[@{corruptionscore}d6&gt;@{corruptionstain}]]}} {{Destruction=[[@{destructionscore}d6&gt;@{destructionstain}]]}} {{Purity=[[@{purityscore}d6&gt;@{puritystain}]]}} {{Vitality=[[@{vitalityscore}d6&gt;@{vitalitystain}]]}} {{Unstained=[[0]]}}"; startRoll(stain_string, roll =&gt; { console.log(roll); let aether_dice = parseInt(roll.results.Aether.dice.length)||0; let aether_results = parseInt(roll.results.Aether.result)||0; let corruption_dice = parseInt(roll.results.Corruption.dice.length)||0; let corruption_results = parseInt(roll.results.Corruption.result)||0; let destruction_dice = parseInt(roll.results.Destruction.dice.length)||0; let destruction_results = parseInt(roll.results.Destruction.result)||0; let purity_dice = parseInt(roll.results.Purity.dice.length)||0; let purity_results = parseInt(roll.results.Purity.result)||0; let vitality_dice = parseInt(roll.results.Vitality.dice.length)||0; let vitality_results = parseInt(roll.results.Vitality.result)||0; let unstained_results = parseInt(roll.results.Unstained.result)||0; let dice_total = aether_dice + corruption_dice + destruction_dice + purity_dice + vitality_dice; let result_total = aether_results + corruption_results + destruction_results + purity_results + vitality_results; let new_unstained = dice_total - result_total; roll.results.Unstained.result = new_unstained; roll.results.Unstained.expression = new_unstained; finishRoll(roll.rollId, { Unstained: new_unstained }); }); } else { const stain_string = "&amp;{template:staincheck} {{nosyp=Your Shade Vessel is Empty. The Shade Denies You.}}"; startRoll(stain_string, roll =&gt; { finishRoll(roll.rollId, {}); }); }; }); }); Roll Template: {{#nosyp}}&lt;div class="sheet-nosyp"&gt;{{nosyp}}&lt;/div&gt;{{/nosyp}}
For the Syphon Roll Button, I am trying this: on('clicked:syphonroll', () =&gt; { getAttrs(['syplevel'], values =&gt; { const syplevel = values.syplevel; const roll_string = `"&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}}"` startRoll(roll_string, roll =&gt; { console.log(roll); let syphonroll = parseInt(roll.results.syphonroll.result)||0; finishRoll(roll.rollId, { if (syplevel &lt; 6) { for (i = 0; i &lt; syphonroll; i++) { syplevel ++; } } }); }); }); }); But every time I try to run it, I keep getting this: And it turns out its being caused by this: How? Why????
1713191919
GiGs
Pro
Sheet Author
API Scripter
The first thing I notice is that you aren't coercing syplevel to a numerical value. You are using const syplevel = values.syplevel; but probably should be using const syplevel = parseInt(values.syplevel) || 0; or const syplevel = +values.syplevel || 0; The second thing is the extra quotes in this: const roll_string = `"&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}}"` which should be either const roll_string = "&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}}"; or const roll_string = `&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}}`; I notice there you are using `` syntax (template lteral_, but are doing nothing that actually needs it. The syplevel value isn't used in there, so is this right? Finally I notice what is most likely the real problem. You have this line: let syphonroll = parseInt(roll.results.syphonroll.result)||0; for this to be meaningful, there must be in your troll, a section like this: {{syphonroll = [[ something the resolves to a number here ]] )) In the current version, syphonroll is undefined, making the later use in the for loop fail. If you want to use syphenroll you could grab it from the character sheet: add it to getAttrs, like: getAttrs(['syplevel', 'syphonroll'], values =&gt; { const syplevel = +values.syplevel || 0; const syphonroll = +values.syphonroll || 0;
So, Im trying this now...the code doesn't break/disable the whole sheet...but when I click the action button, it doesn't even call the roll... Sheetworker: on('clicked:syphonroll', () =&gt; { const roll_string = `&amp;{template:syphonroll} {{title=Syphon the Realm!}} {{subtitle= @{magename} Drinks Deeply and drains the Realm for [[@{syphonroll}d6&gt;4]] Syp Levels!}} {{syplevel=[[0]]}}` startRoll(roll_string, roll =&gt; { console.log(roll); let syphonlvl = parseInt(roll.results.syphonroll.result) || 0; getAttrs([syplevel]) ;{values =&gt; { let syplevel = parseInt(values.syplevel) || 0; if (syplevel &lt; 6) { for (let i = 0; i &lt;= syphonlvl; i++) { syplevel++; } } else syplevel = 6; }; setAttrs({ new_syp: syplevel }); } finishRoll(roll.rollId, { syphonlvl: new_syp }); }); }); CSS: .sheet-rolltemplate-syphonroll { color: white; font-family: Cinzel; height: 200px; background-position: center center; background: url(<a href="https://files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787" rel="nofollow">https://files.d20.io/images/387718371/YUXVmE5_RKEaaOy_UEcCVA/thumb.png?1712432787</a>) no-repeat; background-size: 100% 100%; background-color: #333333; } .sheet-rolltemplate-syphonroll.sheet-header { background-color: var(header-bg-color); color: var(header-text-color); vertical-align: center; text-transform: capitalize; padding: 5px; border-bottom: 2px solid black; } .sheet-rolltemplate-syphonroll .sheet-title { vertical-align: middle center; font-size: 18pt; font-weight: italic; } .sheet-rolltemplate-syphonroll .sheet-subtitle { vertical-align: middle center; font-size: 10pt; } RollTemplate: &lt;button type="action" name="attr_syphonroll"&gt;Syphon&lt;/button&gt; &lt;rolltemplate class="sheet-rolltemplate-syphonroll"&gt; &lt;div class="sheet-container" &gt; &lt;div class="sheet-header"&gt; {{#title}}&lt;div class="sheet-title"&gt;{{title}}&lt;br&gt;&lt;/div&gt;{{/title}} {{#syplevel}}&lt;div class="syplevel"&gt;{{syplevel}}&lt;/div&gt;{{/syplevel}} {{#subtitle}}&lt;div class="sheet-subtitle"&gt;&lt;br&gt;&lt;br&gt;{{subtitle}}&lt;/div&gt;{{/subtitle}} &lt;/div&gt; &lt;/div&gt; &lt;/rolltemplate&gt; The issue really is that I can't even get an object to be generated...
1714167448

Edited 1714244908
GiGs
Pro
Sheet Author
API Scripter
I'll have a look at this in a bit. But in the meantime, you can use the console.log command to check if something is being triggered, like on('clicked:syphonroll', () =&gt; { console.log('clicked syphonroll'); then look in the console (press f12 and select console tab) to see if that message pops up when you press the button.
1714245334

Edited 1714245451
GiGs
Pro
Sheet Author
API Scripter
The first problem I see is this, right need the start: let syphonlvl = parseInt(roll.results.syphonroll.result) || 0; That requires there to be a {{syphonlvl= in your roll, but there isnt one, so this will be undefined . This might work: let syphonlvl = parseInt(roll.results.subtitle.result) || 0; since i think the value of {{subtitle}} will grab the first inline roll in that section. It's worth a try. The second problem is this line: getAttrs([syplevel]) ;{values =&gt; { there is no syplevel variable defined in the javascript . There is a {{syplevel=[[0]]}} in the roll string, but getAttrs can't see that, and even if it could, the value would always equal 0. Actually, the syntax of that line is completely wrong, too. It should be: getAttrs([syplevel], values =&gt; { but that assumes there is a variable syplevel already defined, and that variable contains a valid attribute name.
1714245706
GiGs
Pro
Sheet Author
API Scripter
There'sd another structural problem. Everythign that wants to use anything generated by the getAttrs must be inside getAttrs. It looks like you have a getAttrs followed by a finishRoll and setAttrs - that won't work.