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

Custom Sheet javascript determine Dice Roll based on...

Hello, I have the following setup and I'm trying to get it to work.... Goal: Determine the dice to be rolled based on an attribute. The attribute is determined based on some math including two other attributes. attr_sub_skill_attack_dice_cost (How many points it takes to increase dice by 1) attr_sub_skill_attack_dice_paid (how many points have been paid into the cost). attr_sub_skill_attack_dice_total (this should be *.cost / *.paid) Based on the Total, java script should read the number from the total and pick the dice from the array. This is the code I am starting with.... < input type =" number " name =" attr_sub_skill_attack " value =" 10 "> < input name =" attr_sub_skill_attack_dice " value ="" readonly > < button type =" roll " value =" [[@{sub_skill_attack_dice}]] " name =" roll_sub_skill_attack_dice "> Test Roll </ button > < script type =" text/worker ">     on ( ' change:sub_skill_attack_dice_paid ' ) , function ( eventInfo ) {         getAttrs ([ ' sub_skill_attack_dice_cost ' , ' sub_skill_attack_dice_paid ' ] , function ( values ) {             setAttrs ( { sub_skill_attack_dice_total : Math . floor ( values . sub_skill_attack_dice_cost / sub_skill_attack_dice_paid ) } ) ;         } ) ;     } ) ;     const dice_values = [ ' 1 ' , ' 1D2 ' , ' 1D4 ' , ' 1D6 ' , ' 1D8 ' , ' 1D10 ' , ' 1D12 ' , ' 2D6 ' , ' 2D8 ' , ' 3d6 ' , ' 2D10 ' , ' 2D12 ' , ' 3d8 ' , ' 4D6 ' , ' 3d10 ' , ' 5d6 ' , ' 4D8 ' , ' 3d12 ' , ' 6dd ' , ' 4D10 ' , ' 7d6 ' , ' 4d12 ' , ' 6D8 ' , ' 8D6 ' , ' 5d10 ' , ' 7d8 ' , ' 5D12 ' , ' 6d10 ' , ' 8D8 ' , ' 7D10 ' , ' 6D12 ' , ' 8d10 ' , ' 7d12 ' , ' 9D10 ' , ' 8d12 ' , ' 10d10 ' , ' 9d12 ' , ' 11D10 ' , ' 10D12 ' , ' 12D10 ' , ' 12d12 ' , ' 8d20 ' , ' 10d20 ' , ' 12d20 ' , ' 20d20 ' ] ;     const stat_names = [ ' sub_skill_attack ' ] ;     const name_end = ' dice ' ; //if you want to use a different name end, change it here.     stat_names . forEach (stat => {         on ( ` change: ${ stat }` , () => {             getAttrs ([ stat ] , v => {                 const score = + v [ stat ] || 0 ;                 const dice = dice_values [ Math . max ( 0 , Math . min ( dice_values . length - 1 , score ))] ;                 setAttrs ( {                     [ `${ stat } _ ${ name_end }` ] : dice                 } ) ;             } ) ;         } ) ;     } ) ; </ script > And this is what I have now.       </ td >                     < td class =" skill-input ">< input class =" inputfield text " style =" width: 50px; " type =" number " default =" 0 " name =" attr_skillPoints " min =" 0 " max =" 999 "/></ td >                 </ tr >                 < tr >                     < td colspan =" 8 " ; class =" subheading " style =' line-height: 40px; position: centered; '> Ability Sub-Skills </ td >                 </ tr >                 < tr >                     < td class =" ability-subSkillProperty "> SubSkill Rolls </ td >                                         < td >< button type =" roll " value =" [[@{sub_skill_attack_dice_total}]] " name =" roll_sub_skill_attack_dice_total "> Test Roll </ button ></ td >                     < td ></ td >                 </ tr >                 < tr >                     < td > Dice Cost </ td >                     < td >< input type =" number " name =" attr_sub_skill_attack_dice_cost " value =" 5 "></ td >                                     </ tr >                 < tr >                     < td > Dice Paid </ td >                     < td >< input type =" number " name =" attr_sub_skill_attack_dice_paid " value =" 10 "></ td >                                     </ tr >                 < tr >                     < td > Dmg Dice </ td >                                         < input name =" attr_sub_skill_attack_dice_total " value ="" readonly >                 </ tr > on ( ' change:sub_skill_dice_cost change:sub_skill_attack_dice_paid ' , function ( eventInfo ) {     getAttrs ([ ' sub_skill_attack_dice_cost ' , ' sub_skill_attack_dice_paid ' ] , function ( values ) {               let cost = parseInt ( values . sub_skill_attack_dice_cost )         let paid = parseInt ( values . sub_skill_attack_dice_paid )                         setAttrs ( { sub_skill_attack_dice_total : Math . floor ( cost / paid ) } ) ;     } ) ; } ) ; const dice_values = [ ' 1 ' , ' 1D2 ' , ' 1D4 ' , ' 1D6 ' , ' 1D8 ' , ' 1D10 ' , ' 1D12 ' , ' 2D6 ' , ' 2D8 ' , ' 3d6 ' , ' 2D10 ' , ' 2D12 ' , ' 3d8 ' , ' 4D6 ' , ' 3d10 ' , ' 5d6 ' , ' 4D8 ' , ' 3d12 ' , ' 6dd ' , ' 4D10 ' , ' 7d6 ' , ' 4d12 ' , ' 6D8 ' , ' 8D6 ' , ' 5d10 ' , ' 7d8 ' , ' 5D12 ' , ' 6d10 ' , ' 8D8 ' , ' 7D10 ' , ' 6D12 ' , ' 8d10 ' , ' 7d12 ' , ' 9D10 ' , ' 8d12 ' , ' 10d10 ' , ' 9d12 ' , ' 11D10 ' , ' 10D12 ' , ' 12D10 ' , ' 12d12 ' , ' 8d20 ' , ' 10d20 ' , ' 12d20 ' , ' 20d20 ' ] ;     let cost = parseInt (values . sub_skill_attack_dice_cost) || 0 ;     let paid = parseInt (values . sub_skill_attack_dice_paid) || 0 ;         let total = cost / paid const stat_names = [ ' sub_skill_attack_dice_total ' ] ;     const name_end = ' total ' ; //if you want to use a different name end, change it here.     stat_names . forEach (stat => {         on ( ` change: ${ stat }` , () => {             getAttrs ([ ' stat ' , ' sub_skill_dice_cost ' , ' sub_skill_dice_paid ' ] , v => {                                 const score = + v [ stat ] || 0 ;                 const dice = dice_values [ Math . max ( 0 , Math . min ( dice_values . length - 1 , score ))] ;                 setAttrs ( {                     [ `${ stat } _ ${ name_end }` ] : dice                 } ) ;             } ) ;         } ) ;     } ) ; I don't know where to modify the above so it all references correctly. :(
1646860536
GiGs
Pro
Sheet Author
API Scripter
You have a problem with ordering. You can only get attribute values inside a getAttrs function. You have these lines let cost = parseInt (values . sub_skill_attack_dice_cost) || 0 ; let paid = parseInt (values . sub_skill_attack_dice_paid) || 0 ;    before the sheet worker starts, so they won't work. Sheet works know nothing about the character sheet - they cant automatically know attributes and their values. You have to extract the values with getAttrs before you can do anything with them. So you need to put these lines inside the worker, like getAttrs ([ 'stat' , 'sub_skill_dice_cost' , 'sub_skill_dice_paid' ], v => {             let cost = parseInt ( values . sub_skill_attack_dice_cost ) || 0 ;             let paid = parseInt ( values . sub_skill_attack_dice_paid ) || 0 ;                 let total = cost / paid ;                             const score = + v [ stat ] || 0 ;             const dice = dice_values [ Math . max ( 0 , Math . min ( dice_values . length - 1 , score ))]; I cant comment if there are other problems, I don't really follow what you are trying todo (for instance, I don't see where the total variable is used). So there miught be other problems, but that is the big one that jumped out at me.
Thanks. Basically those two attributes will have fillable fields they are the. Divided be each other which produces the sub_skill_attack_dice_total (just a number) Then the script should read the total and select the dice type from that total.
1646862647
GiGs
Pro
Sheet Author
API Scripter
Also in your second screenshot, the sheet workers arent inside a script block (which looks like <script type+"text/worker"> </script> Is that an artifact of cut&paste, or are you really missing a script block?
1646862795
GiGs
Pro
Sheet Author
API Scripter
Do you more stats like sub_skill_attack_dice_total Just wondering why you are using a forEach loop there. Also, is this structure inside a repeating section?
All the watchers are goin to be at the bottom of the script, however it is being used by a repeating field... is that not gonna work?
GiGs said: Also in your second screenshot, the sheet workers arent inside a script block (which looks like <script type+"text/worker"> </script> Is that an artifact of cut&paste, or are you really missing a script block? I copied them out, but they are in a script block at the bottom of the sheet.
The script may not be exactly what is needed. I copied it from somewhere that did SOMETHING like what I wanted. However, this watcher is taking an attribute and producing a dice pair base on the attributes score. What I'm trying to do is take 2 attributes, divide them against each other (Cost / Paid) which = sub_skill_attack_dice_total That attr "sub_skill_attack_dice_total" is then what would replace the Str or Dex attribute that is used by the original script. But there are some syntax in here that I'm just not understanding, so I don't know where to inject my variable/attribute/aliases.
1646876792
GiGs
Pro
Sheet Author
API Scripter
I've had a closer look and I see a few more problems. First, the code in your second worker is designed to be expanded to cover multiple original stats (it use forEach to create the worker, and you only do that when creating multiple workers that have a similar structure, and are used to create multiple stats). But the code in your first worker is designed to create a single attribute, so there is probably a mismatch here. Second both workers have the setAttrs pointing to the same attribute name, sub_skill_attack_dice_total, so they'll be overwriting each other. Then this line getAttrs(['stat', 'sub_skill_dice_cost', 'sub_skill_dice_paid'], v => { should be getAttrs([stat, 'sub_skill_dice_cost', 'sub_skill_dice_paid'], v => { Those quotes on the first attribute are really important. Look at the forEah line, it creates a variable stat , and this is what is expected here. But by putting quotes around it, it is looking for an attribute on the sheet named stat , which doesnt exist. There might be more issues. It seems to be you can simplify these workers into a single worker, like so:          const dice_values = [ '1' , '1D2' , '1D4' , '1D6' , '1D8' , '1D10' , '1D12' , '2D6' , '2D8' , '3d6' ,              '2D10' , '2D12' , '3d8' , '4D6' , '3d10' , '5d6' , '4D8' , '3d12' , '6dd' , '4D10' , '7d6' , '4d12' ,              '6D8' , '8D6' , '5d10' , '7d8' , '5D12' , '6d10' , '8D8' , '7D10' , '6D12' , '8d10' , '7d12' , '9D10' ,              '8d12' , '10d10' , '9d12' , '11D10' , '10D12' , '12D10' , '12d12' , '8d20' , '10d20' , '12d20' , '20d20' ];         on ( 'change:sub_skill_dice_cost change:sub_skill_attack_dice_paid' , function ( eventInfo ) {             getAttrs ([ 'sub_skill_attack_dice_cost' , 'sub_skill_attack_dice_paid' ], function ( values ) {                       const cost = parseInt ( values . sub_skill_attack_dice_cost );                 const paid = parseInt ( values . sub_skill_attack_dice_paid ) || 1 ;                 const score = Math . floor ( cost / paid );                 const lookup_value = Math . max ( 0 , Math . min ( dice_values . length - 1 , score ));                 const dice = dice_values [ lookup_value ];                 setAttrs ({                     sub_skill_attack_dice_total : dice ,                 });             });         }); In this worker, I split the dice dice_values line into two - this is so you can check the lookup_value by logging, if the worker is still not working. I also added || 1 to the paid variable. This means it takes a default value of 1, if its actually 0. Since its a divisor, if that is empty or a zero, the worker will break. Give that a try and see if it does what you need.
1646876916
GiGs
Pro
Sheet Author
API Scripter
I just noticed you said this is in a repeating section. That requires attribute names on the change and getAttrs lines to be written differently. What is the name of the repeating section?
1646877650

Edited 1646877797
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
So, what problem are you currently having? One thing I notice is that I think you want to flip your division. Dividing cost by paid (e.g. cost / paid) will give you large numbers when very little has been paid, and small numbers when more has been paid, with fractions occurring after paid equals cost. Additionally, this will error out when nothing has been paid because of dividing by zero. I think what you want is to do: Math.floor(paid / cost) Now, what I think you're trying to do is lookup in the array based on how many ranks have been purchased. It's not really my preferred code, but your first bit of code in the OP seems close to what you want, it just had some code issues. I'd recommend something like this: <input type="number" name="attr_sub_skill_attack" value="10"> <input name="attr_sub_skill_attack_dice" value="" readonly> <button type="roll" value="[[@{sub_skill_attack_dice}]]" name="roll_sub_skill_attack_dice">Test Roll</button> <script type="text/worker"> //I like to put my block scope variables at the top so I know where to find all of them. const dice_values = ['1', '1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '2d6', '2d8', '3d6','2d10', '2d12', '3d8', '4d6', '3d10','5d6','4d8', '3d12', '6dd', '4d10', '7d6', '4d12','6d8', '8d6', '5d10', '7d8', '5d12', '6d10', '8d8', '7d10', '6d12', '8d10', '7d12', '9d10', '8d12', '10d10', '9d12', '11d10', '10d12', '12d10', '12d12', '8d20', '10d20', '12d20', '20d20']; const stat_names = ['sub_skill_attack']; const name_end = 'dice'; //if you want to use a different name end, change it here. ['cost','paid'].forEach((suffix)=>{//iterate through the two suffixes to create a listener for each. This cuts down on the code we have to write on(`change:sub_skill_attack_dice_${suffix}`,()=>{//We don't need the event object here, so I'm not passing it getAttrs(['sub_skill_attack_dice_cost', 'sub_skill_attack_dice_paid'],(values)=>{ const setObj = {};//I prefer to make an object that will store my changes so that I can accumulate them easily and even log them to verify things before setting them. setObj.sub_skill_attack_dice_total = Math.floor(values.sub_skill_attack_dice_paid / values.sub_skill_attack_dice_cost);//Flipped to the division I think you actually want. Also, you were missing a values call for one of your attribute calls here. setAttrs(setObj); }); }); }); stat_names.forEach(stat => {//As GiGs said, if you only need a single attribute listener here, then you don't need the forEach and can just write it out as a straight string. on(`change:${stat}`, () => { getAttrs([stat], (v) => { const setObj = {}; const score = +v[stat] || 0; setObj[`${stat}_${name_end}`] = dice_values[Math.max(0, Math.min(dice_values.length -1, score))]; setAttrs(setObj); }); }); }); </script> Now, like GiGs, I'm not seeing where the dice_total attribute is supposed to come into this. And, as for using this with a repeating section, my recommendation would be to do something like this: const parseTriggerName = function(string){ let match = string.match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/); match.shift(); return match; }; stat_names.forEach(stat => {//As GiGs said, if you only need a single attribute listener here, then you don't need the forEach and can just write it out as a straight string. on(`change:repeating_section:${stat}`, (event) => {//Actually going to use the event object here. Replace the "section" with the name of your actual repeating section. const [section,id,field] = parseTriggerName(event.sourceAttribute);//Note that these attributes must be in this order, but are each optional, simply leave the space blank if you don't need that bit of the repeating attribute name. E.g. const [,id] = parseTriggerName(event.sourceAttribute) would give you just the id. console.log(section)// => repeating_section console.log(id)// => The row ID (e.g. -opiuLKJ-234llklj) console.log(field)// => sub_skill_attack getAttrs([`${section}_${id}_${stat}`], (v) => { const setObj = {}; const score = +v[`${section}_${id}_${stat}`] || 0; setObj[`${section}_${id}_${stat}_${name_end}`] = dice_values[Math.max(0, Math.min(dice_values.length -1, score))]; setAttrs(setObj); }); }); }); EDIT: Had forgotten to include the parseTriggerName function.
WOW guys!  Amazing!  I'll test this code out tonight!  (sneaking back to work now)