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

Javascript help!

Hello, I have a dice progression chart that I would like to autopopulate based on another field. The field is always a number, but is different skills or attributes. Here is the current code, which I do not understand how to use. This dice progression is based on attr_pistol_tech_level (as an example). If the attri_pistol_tech_level = 16 , it would be = (2D20+1D10) on the dice progression chart, and the attr_pistol_damage field should then populate as (2D20+1D10) Here is the code I currently have. const dice_values = [ ' 1d4 ' , ' 1d6 ' , ' 1d8 ' , ' 1d10 ' , ' 1d12 ' , ' 1d20 ' , ' 1D20+1D4 ' , ' 1D20+1D6 ' , ' 1D20+1D8 ' , ' 1D20+1D10 ' , ' 1D20+1D12 ' , ' 1D20+1D20 ' , ' 2D20+1D4 ' , ' 2D20+1D6 ' , ' 2D20+1D8 ' , ' 2D20+1D10 ' , ' 2D20+1D12 ' , ' 2D20+2D20 ' , ' 3D20+1D4 ' , ' 3D20+1D6 ' , ' 3D20+1D8 ' , ' 3D20+1D10 ' , ' 3D20+1D12 ' , ' 3D20+3D20 ' , ' 4D20+1D4 ' , ' 4D20+1D6 ' , ' 4D20+1D8 ' , ' 4D20+1D10 ' , ' 4D20+1D12 ' , ' 4D20+4D20 ' , ' 5D20+1D4 ' , ' 5D20+1D6 ' , ' 5D20+1D8 ' , ' 5D20+1D10 ' , ' 5D20+1D12 ' , ' 5D20+5D20 ' , ' 6D20+1D4 ' , ' 6D20+1D6 ' , ' 6D20+1D8 ' , ' 6D20+1D10 ' , ' 6D20+1D12 ' , ' 6D20+6D20 ' , ' 7D20+1D4 ' , ' 7D20+1D6 ' , ' 7D20+1D8 ' , ' 7D20+1D10 ' , ' 7D20+1D12 ' , ' 7D20+7D20 ' , ' 8D20+1D4 ' , ' 8D20+1D6 ' , ' 8D20+1D8 ' , ' 8D20+1D10 ' , ' 8D20+1D12 ' , ' 8D20+8D20 ' , ' 9D20+1D4 ' , ' 9D20+1D6 ' , ' 9D20+1D8 ' , ' 9D20+1D10 ' , ' 9D20+1D12 ' , ' 9D20+9D20 ' , ' 10D20+1D4 ' , ' 10D20+1D6 ' , ' 10D20+1D8 ' , ' 10D20+1D10 ' , ' 10D20+1D12 ' , ' 10D20+10D20 ' ,     ] ;     const stat_names = [ ' dice_progression_chart ' ] ;     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 ' , ' sub_skill_attack_dice_total ' ] , v => {                 let cost = parseInt ( values . sub_skill_attack_dice_cost ) || 0 ;                 let paid = parseInt ( values . sub_skill_attack_dice_paid ) || 0 ;                     let dice_level = cost / paid ;                                 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'm looking at the code above, I'm hoping there is something a little bit more universal that can be done. I have a LOT of these, and repeating fields, that needs to be able to pull from the Dice Progression Chart based on that skill or abilities Tech Level.
The end goal is that it looks up the Tech Level defined in A field, not caring about the attr_name and just reading the field data, then pulling up the correlated Dice data for that level. If read data = 16 ;     Echo (2D20+1D10) would be the long way to do this, 256 different levels....
1667612061

Edited 1667612366
Oosh
Sheet Author
API Scripter
What's the list of attribute names you need to be able to feed through the function? Are they programmatically named? Is it always attr_${skill}_tech_level and attr_${skill}_damage ? What's the cost and skill stuff about in the code above? It doesn't seem relevant to the description of the math in your post? Here's how I'd go about it, by splitting the code into smaller functions, explicitly named so your actual event handlers are extremely short and easy to read: Don't worry too much about the skillProgressionDiceExpressions at the top - it's just a programmatic way of generating the array you've already got. I'm allergic to hard-coding that kind of thing: // skillProgressionDiceExpressions is the same as your existing array of strings, don't feel the need to change it if you're happy with it // I just don't like hard-coding const dieSizesForSkillProgression = [ 4, 6, 8, 10, 12, 20 ]; const skillProgressionDiceExpressions = Array(256).fill().map((v, i) => { const fullDice = Math.floor((i + 1)/dieSizesForSkillProgression.length); const extraDieSize = dieSizesForSkillProgression[i % dieSizesForSkillProgression.length]; const useExtraDie = i % dieSizesForSkillProgression.length < (dieSizesForSkillProgression.length - 1); return `${fullDice ? `${fullDice}d20` : ''}${fullDice && useExtraDie ? ' + ' : ''}${useExtraDie ? `d${extraDieSize}` : ''}`; }); // Little functions like these are handy to have, it'll reduce repetition const bindNumberToRange = (value, min, max) => { if (typeof(value) !== 'number') return value; if (typeof(min) === 'number') value = Math.max(value, min); return (typeof(max) === 'number') ? Math.min(value, max) : value; } // Be explicit with naming functions. TTRPG math can get hairy, don't make it more difficult for yourself with // function names like 'convAttr()' ... convert attribute to what? Keyboards are cheap! const getDiceExpressionBySkillLevel = (skillLevel) => { skillLevel = parseInt(skillLevel); if (typeof(skillLevel) !== 'number') return null; return skillProgressionDiceExpressions[bindNumberToRange(skillLevel, 1, 256)]; } // And finally, the function that does what you want it to const setDamageAttributeBySkillLevel = (techSkillAttributeName, damageAttributeName) => { getAttrs([ techSkillAttributeName, damageAttributeName ], originalValues => { const newDiceExpression = getDiceExpressionBySkillLevel(originalValues[techSkillAttributeName]); if (newDiceExpression) setAttrs({ [damageAttributeName]: newDiceExpression }); }); } // This part requires programmatic naming of your attributes to be done easily const skillNames = [ 'pistol', 'rifle', 'space_magic', 'bread_toasting', 'egg_poaching' ]; // Finally, iterate over all skills with matching attribute name format and apply the handler skillNames.forEach(skillName => on(`change:attr_${skillName}_tech_level`, setDamageAttributeBySkillLevel(`attr_${skillName}_tech_level`, `attr_${skillName}_damage`))); By splitting the logic out and making your handler as slim as possible, you're also increasing opportunity for code re-use. For example, if you find you need this logic to run the first time a sheet is opened, you already have the setDamageAttributeBySkillLevel function to reach for, meaning minimal extra code to throw some extra functionality in there.