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

Sheetworker to activate on a rollbutton

1659480627
Aero
Pro
Hopefully a quickie... So I have the following rollbutton: <button type="roll" value="/r (((1 - @{acuityused}) * @{acuity}) + ((1 - @{heartused}) * @{heart}))d6s" name="talkingRoll">Talking</button>  When the button is clicked it does the roll and it works fine. What I'd now like to do is for a couple of checkboxes to be ticked as a result of the rollbutton being activated. Player clicks the rollbutton, the die values come up and two checkboxes become ticked (whether or not they were already ticked). Here's the sheetworker that isn't working: on('clicked:talkingRoll', function() { getAttrs(['heartused', 'acuityused'], v=> { setAttrs({ heartused: 1, acuityused: 1 }); }); }); To sum up – I want this sheetworker to check both heartused and acuityused   checkbox inputs whenever talkingRoll is clicked, regardless of whether they were already ticked or not. Where have I gone wrong with this? ... and yes, I know I don't need to getAttrs for this, I just don't know how to safely remove it. Happy to be enlightened!
1659484193
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
To do this, you'll need an action button and use Custom Roll Parsing to send the roll.
1659500289
GiGs
Pro
Sheet Author
API Scripter
As Scott says, you'll need to use a different method - roll buttons can only print to chat, and action buttons (originally) can only run sheet workers (which can't print to chat). Custom Roll Parsing is a relatively new feature that can do both, but is a lot more complicated to write. Since you aren't sure about your last sheet worker, I think you dould do with some help there. But first, if you last sheet worker had worked, you could do this: on('clicked:talkingRoll', function() { setAttrs({ heartused: 1, acuityused: 1 }); }); That's how you avoid the getAttrrs - just use setAttrs directly.
1659516026
Aero
Pro
Ah yes, I forgot about CRP. But I'm going to want it to work in repeating sections and we never did figure that out ( previously ). Oh well, it's not crucial. Thanks anyway. And thanks for the getAttrs tip!
1659526030

Edited 1659527686
GiGs
Pro
Sheet Author
API Scripter
I see that thread trailed off at the end, I must have got distracted, or didn't get enough information to proceed. You can do everyting in a repeatign section that you can do outside of one, but the syntax can be more complex. You'd need to describe what you need in as much detail as is necessary - describe exactly what you need, and supply all relevant HTML, then we can build it.
1659527401

Edited 1659527524
Oosh
Sheet Author
CRP buttons should work fine in repeating sections - I used loads of them in a sheet. I'm not sure what your repeating section is called, but you want something like this: <fieldset class="repeating_mysection"> <button type="action" name="act_talkingroll">Talking</button> </fieldset> And then your event listener needs the name of the repeating section, and the name of the action button: on('clicked:repeating_mysection:talkingroll', talkingRoll); and finally the function called by the listener: const talkingRoll = (event) => { // log the event parameter so you can see what it contains - looks like you don't need it for this particular function though console.log(event); // send the roll with startRoll - the value="" contents from the roll button // do your setAttrs stuff - what GiGs posted } You can leave the function anonymous inside the event listener if you prefer (the way it is now), but named functions give you better stack traces and generally make your code easier to read.
1659536699
Aero
Pro
Here's the repeating section: <fieldset class="repeating_belongings"> <input type="text" name="attr_gear" style="width:125px;">   <input type="checkbox" name="attr_gun" value="1"><span></span>             <select name="attr_gearquality" style="width:175px;">   <option value="0">--quality--</option>   <option value="1">Crap: 1d4</option>   <option value="2">Normal: 1d6</option>   <option value="3">Normal & Excellent: 2d6</option>   <option value="4">Big: 1d8</option>   <option value="5">Big & Excellent: 2d8</option> <input class="sheet-si sheet-si0" type="checkbox" name="attr_gearquality" value="0"><!-- SI0 --> <input class="sheet-si sheet-si1" type="checkbox" name="attr_gearquality" value="1"><!-- SI1 --> <input class="sheet-si sheet-si2" type="checkbox" name="attr_gearquality" value="2"><!-- SI2 --> <input class="sheet-si sheet-si3" type="checkbox" name="attr_gearquality" value="3"><!-- SI3 --> <input class="sheet-si sheet-si4" type="checkbox" name="attr_gearquality" value="4"><!-- SI4 --> <input class="sheet-si sheet-si5" type="checkbox" name="attr_gearquality" value="5"><!-- SI5 -->   <div class="sheet-sishow-SI0"></div>   <div class="sheet-sishow-SI1"><button type="roll" value="/r (1 - @{gearused})d4s + ((1 - @{gearused}) * @{gun})d4s" name="gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div>   <div class="sheet-sishow-SI2"><button type="roll" value="/r (1 - @{gearused})d6s + ((1 - @{gearused}) * @{gun})d4s" name="gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div>   <div class="sheet-sishow-SI3"><button type="roll" value="/r ((1 - @{gearused}) * 2)d6s + ((1 - @{gearused}) * @{gun})d4s" name="gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div>   <div class="sheet-sishow-SI4"><button type="roll" value="/r (1 - @{gearused})d8s + ((1 - @{gearused}) * @{gun})d4s" name="gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div>   <div class="sheet-sishow-SI5"><button type="roll" value="/r ((1 - @{gearused}) * 2)d8s + ((1 - @{gearused}) * @{gun})d4s" name="gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div> </fieldset> So what I'm after is a button that will perform the roll as shown and check the corresponding gearused checkbox. Note that there are five rollbuttons available in each row of the repeating section – whatever option is selected in the  attr_gearquality  input determines which button is displayed (and thus what roll will be performed). So, if 'Normal & Excellent' is selected then when the button is clicked the sheetworker should roll 2d6 and check the corresponding gearused checkbox. Hope that all makes sense.
1659540262

Edited 1659540658
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
In your first post, you showed wanting to mark specific attributes (heartused and acuityused) as checked. But in your most recent reply, you mention wanting to check the gearused attribute. I did the below based on the OP description of what attributes to check. I'll note that in your demo code, all the gearused checkboxes have the same checked value, so you'll want to change that if you actually want to manipulate those. Also, with the action button method, you only need a single button instead of one for each roll type because the sheetworker dynamically constructs the roll based on the state of the item. <fieldset class="repeating_belongings"> <input type="text" name="attr_gear" style="width:125px;">   <input type="checkbox" name="attr_gun" value="1"><span></span> <select name="attr_gearquality" style="width:175px;"> <option value="0">--quality--</option> <option value="1">Crap: 1d4</option> <option value="2">Normal: 1d6</option> <option value="3">Normal & Excellent: 2d6</option> <option value="4">Big: 1d8</option> <option value="5">Big & Excellent: 2d8</option> </select><!-- Was missing the closure to the select tag here --> <!-- Note I've removed these control inputs to shorten the code. You may not even need them when using the action button/CRP method --> <div class="sheet-sishow-SI0"></div> <div class="sheet-sishow-SI2"><button type="action" name="act_gearRoll">Roll</button>  <input type="checkbox" name="attr_gearused" value="1"><span></span> </div> </fieldset> <<script type="text/worker"> //Utility function from the K-scaffold. Parses out the section name, row id, and attribute name from an event's triggerName or sourceAttribute. const parseTriggerName = function(string){ let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/); match.shift(); return match; }; on('clicked:repeating_belongings:gearroll',(event)=>{ //Get our row information; we may not need all of this, but showing how you'd get all of it. //section = `repeating_belongings`; rowID = the rowID; buttonName = gearroll const [section,rowID,buttonName] = parseTriggerName(event.triggerName); //The callback for this getAttrs is going to be async so that we can use the async/await pattern with startRoll getAttrs([`repeating_belongings_${rowID}_gearquality`],async (attributes)=>{ //Convert the value to a number attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_${rowID}_gearquality`]; //array to use to lookup how many dice of what size to roll const diceLookup = [ {size:4,multiplier:1}, {size:6,multiplier:1}, {size:6,multiplier:2}, {size:8,multiplier:1}, {size:8,multiplier:2}, ]; //Extract the appropriate details const dieDetails = diceLookup[attributes[`repeating_belongings_${rowID}_gearquality`] - 1]; if(!dieDetails) return; //If there are no details, then don't send a message //Construct our message. The roll has to be in a roll template for CRP to work. const message = `&{template:default} {{name=@{repeating_belongings_${rowID}_gear}}} {{Roll=[[[[(1 - @{repeating_belongings_gearused}) * ${dieDetails.multiplier}]]d${dieDetails.size} + [[(1 - @{repeating_belongings_gearused}) * @{repeating_belongings_gun}]]d4]]}}`; //Send the roll, and wait for the results to come back const result = await startRoll(message); finishRoll(result.rollId);//We aren't doing any manipulation of the roll, so we can just finish it right away setAttrs({//Set your attributes heartused: 1, acuityused: 1 }); }); }); </script> EDIT: Had forgotten to include the repeating row details for calling gearused and gun in the message
1659540402
GiGs
Pro
Sheet Author
API Scripter
What do you mean by this? " check the corresponding gearused checkbox" - while there are five inputs named gearused there, they all have the same name so if any one is checked, they are all checked. Is that okay? Your rolls look like a very convolted way to generate 0 if gearused is unchecked. If you are using CRP, you can ignore that - just enter a roll assuming the checkbox is checked, and in the CRP worker you can check the value of gearused, and cancel the roll if unchecked (and even send different text if desired). Another question: what is the purpose of this: <input class="sheet-si sheet-si0" type="checkbox" name="attr_gearquality" value="0"><!-- SI0 --> <input class="sheet-si sheet-si1" type="checkbox" name="attr_gearquality" value="1"><!-- SI1 --> <input class="sheet-si sheet-si2" type="checkbox" name="attr_gearquality" value="2"><!-- SI2 --> <input class="sheet-si sheet-si3" type="checkbox" name="attr_gearquality" value="3"><!-- SI3 --> <input class="sheet-si sheet-si4" type="checkbox" name="attr_gearquality" value="4"><!-- SI4 --> <input class="sheet-si sheet-si5" type="checkbox" name="attr_gearquality" value="5"><!-- SI5 --> Why do you have multiple checkboxes with different values. I can see the value is set in the select, but what is this checkbox for?
1659545359
Aero
Pro
Right, yes I wasn't clear at all was I? The purpose of the <input class="sheet-si sheet-si0" type="checkbox" name="attr_gearquality" value="0"><!-- SI0 --> lines is to display the rollbutton corresponding to the gearquality select field. I didn't invent it and I don't fully understand it but it works (with accompanying CSS that I've neglected to share). But as Scott C noted, this won't be necessary with CRP – there can just be a single action button and the sheetworker can work out how many of which dice to roll.  By  " check the corresponding gearused checkbox" I meant the one in the same row of the repeating section. Those five instances of gearused are only for formatting purposes (if I put it outside the rollbutton selection div then it inserts a carriage return – there's probably another way around this via CSS, but duplicating the checkbox is harmless). So in other words, clicking the action button in a particular row should check the gearused box in the same row.
1659545456
Aero
Pro
Thanks very much for the code Scott C. I don't have time to implement and test it before the game in question, but it will be a good thing to learn for future projects. I'll let you know how I get on.
1659628067
Aero
Pro
Thanks again Scott C, that works and replicates the roll as required. And indeed I can now remove the control inputs so there's just one instance of the button and one instance of the checkbox (per row). I can't seem to find the correct syntax to check the gearused checkbox in the corresponding row though. I've tried using "repeating_belongings_${rowID}_gearused" under setAttrs and that's proving insufficient. What should it be?   //Utility function from the K-scaffold. Parses out the section name, row id, and attribute name from an event's triggerName or sourceAttribute.   const parseTriggerName = function(string){     let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);     match.shift();     return match;   };   on('clicked:repeating_belongings:gearroll',(event)=>{     //Get our row information; we may not need all of this, but showing how you'd get all of it.     //section = `repeating_belongings`; rowID = the rowID; buttonName = gearroll     const [section,rowID,buttonName] = parseTriggerName(event.triggerName);     //The callback for this getAttrs is going to be async so that we can use the async/await pattern with startRoll     getAttrs([`repeating_belongings_${rowID}_gearquality`],async (attributes)=>{       //Convert the value to a number       attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_${rowID}_gearquality`];       //array to use to lookup how many dice of what size to roll       const diceLookup = [         {size:4,multiplier:1},         {size:6,multiplier:1},         {size:6,multiplier:2},         {size:8,multiplier:1},         {size:8,multiplier:2},       ];       //Extract the appropriate details       const dieDetails = diceLookup[attributes[`repeating_belongings_${rowID}_gearquality`] - 1];       if(!dieDetails) return; //If there are no details, then don't send a message       //Construct our message. The roll has to be in a roll template for CRP to work.       const message = `&{template:default} {{name=@{repeating_belongings_${rowID}_gear}}} {{Roll=[[[[(1 - @{repeating_belongings_gearused}) * ${dieDetails.multiplier}]]d${dieDetails.size} + [[(1 - @{repeating_belongings_gearused}) * @{repeating_belongings_gun}]]d4]]}}`;       //Send the roll, and wait for the results to come back       const result = await startRoll(message);       finishRoll(result.rollId);//We aren't doing any manipulation of the roll, so we can just finish it right away       setAttrs({//Set your attributes             "repeating_belongings_${rowID}_gearused": 1       });     });   });
1659629465

Edited 1659629570
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
When constructing a key from template literals, you need to use bracket syntax for the key value and use backticks instead of quotes: setAttrs({//Set your attributes [`repeating_belongings_${rowID}_gearused`]: 1 });
1659689628

Edited 1659690393
Aero
Pro
Dammit, I screwed up. Got myself confused and wasn't actually testing this sheetworker. Now that I am, I'm afraid it doesn't work at all – no dice roll and no box checked. Would you mind taking another look to see where it's failing?       <div class="traits4">           <div class="sheet-row">  <p style="font-size:120%;"><b>BELONGINGS</b>     <span style="font-size:75%;">Gun?</span></p> <fieldset class="repeating_belongings"> <input type="text" name="attr_gear" style="width:125px;">   <input type="checkbox" name="attr_gun" value="1"><span></span>             <select name="attr_gearquality" style="width:175px;">   <option value="0">--quality--</option>   <option value="1">Crap: 1d4</option>   <option value="2">Normal: 1d6</option>   <option value="3">Normal & Excellent: 2d6</option>   <option value="4">Big: 1d8</option>   <option value="5">Big & Excellent: 2d8</option></select> <button type="action" name="act_gearroll">Roll</button>   <input type="checkbox" name="attr_gearused" value="1"><span></span> </fieldset>       </div>           </div>   //Utility function from the K-scaffold. Parses out the section name, row id, and attribute name from an event's triggerName or sourceAttribute.   const parseTriggerName = function(string){     let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);     match.shift();     return match;   };   on('clicked:repeating_belongings:gearroll',(event)=>{     //Get our row information; we may not need all of this, but showing how you'd get all of it.     //section = `repeating_belongings`; rowID = the rowID; buttonName = gearroll     const [section,rowID,buttonName] = parseTriggerName(event.triggerName);     //The callback for this getAttrs is going to be async so that we can use the async/await pattern with startRoll     getAttrs([`repeating_belongings_${rowID}_gearquality`],async (attributes)=>{       //Convert the value to a number       attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_${rowID}_gearquality`];       //array to use to lookup how many dice of what size to roll       const diceLookup = [         {size:4,multiplier:1},         {size:6,multiplier:1},         {size:6,multiplier:2},         {size:8,multiplier:1},         {size:8,multiplier:2},       ];       //Extract the appropriate details       const dieDetails = diceLookup[attributes[`repeating_belongings_${rowID}_gearquality`] - 1];       if(!dieDetails) return; //If there are no details, then don't send a message       //Construct our message. The roll has to be in a roll template for CRP to work.       const message = `&{template:default} {{name=@{repeating_belongings_${rowID}_gear}}} {{Roll=[[[[(1 - @{repeating_belongings_gearused}) * ${dieDetails.multiplier}]]d${dieDetails.size} + [[(1 - @{repeating_belongings_gearused}) * @{repeating_belongings_gun}]]d4]]}}`;       //Send the roll, and wait for the results to come back       const result = await startRoll(message);       finishRoll(result.rollId);//We aren't doing any manipulation of the roll, so we can just finish it right away       setAttrs({//Set your attributes             [`repeating_belongings_${rowID}_gearused`]: 1       });     });   }); I have other sheetworkers that are fully functional, so it's definitely something in the above code that's not right.
1659710133
Aero
Pro
Oh wait, when you said " The roll has to be in a roll template for CRP to work ", did you mean I actually need to code a rolltemplate rather than leaving it as 'default'? If so, what code in the rolltemplate will get display the rolled values from the sheet worker? I just need the bare minimum really.
1659711336
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
No, since you aren't modifying any of the rolls via a computation after the fact, the default template works fine. The rolls just need to be in a roll template field to be returned as part of the results object. Although, as I'm writing this, it occurs to me that you might be able to do it without a roll template since you aren't modifying any of the rolls.
1659711932
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
As for the cause of the issue, it's cause I had a typo in the code I posted. This: attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_${rowID}_gearquality`]; should have been this: attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_ belongings_ ${rowID}_gearquality`];
1659712349

Edited 1659712508
GiGs
Pro
Sheet Author
API Scripter
This line looks suspect to me: attributes[`repeating_belongings_${rowID}_gearquality`] = +attributes[`repeating_${rowID}_gearquality`]; There's no section name in the part after the equals. Either that line will be caausing a crash, or this line later will. const dieDetails = diceLookup[attributes[`repeating_belongings_${rowID}_gearquality`] - 1]; Because it'll have an undefined value, and you try to subtract 1 from it. Personally I wouldn't save a numerical version back into the same object. I'd so something like const gear_quality = +attributes[`repeating_belongings_${rowID}_gearquality`] || 0; And then use gear_quality later. You don't need to do it this way, but it makes it easier to read and understand what's going on, IMO. Edit: Ninja'd :)
1659714150
Aero
Pro
Amazing, yes, that did it! I also noticed  _ ${rowID}  missing from the roll calculation so I put it in there and it's working perfectly. Thanks both!!