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 Case Statement Confusion

1658043731
GiGs
Pro
Sheet Author
API Scripter
I see that calculation is the same as one I suggested earlier, but I had included a typo (ceil in place of Math.ceil). Is there a question in your last post, or are you showing the final working code?
Now that the upper part (calculating the value of the attributes) works, the lower part (assigning them based on the selected attribute name) has stopped working. I was hoping you might spot an error I'd made, since I can't figure out why that would happen.
1658045928

Edited 1658046028
GiGs
Pro
Sheet Author
API Scripter
These sheet workers are getting too complex to tell just by looking at them. Can you share the current version of the character sheet? Also the CSS so it is displayed properly. Edit: though is it because the stats_core are all in lower case? have you updated that select to use lower case letters. If not, you'll need to have the first letter of each be upper case.
1658050775

Edited 1658050803
Dave
Pro
I'll try to find somewhere I can post it all. Meanwhile, I noticed this in the API console output. It seems to refer to a line from one of the sheetworkers? (I tried changing the attribute names to uppercase, but that unfortunately didn't fix it.) "TypeError: stats_all.reduce(...).slice(...).toLowercase is not a function at eval (eval at messageHandler (evalmachine.<anonymous>:713:6), <anonymous>:48:96) at eval (<anonymous>) at messageHandler (evalmachine.<anonymous>:713:6) at process.<anonymous> (/home/node/d20-api-server/node_modules/tiny-worker/lib/worker.js:65:55) at process.emit (events.js:314:20) at emit (internal/child_process.js:877:12) at processTicksAndRejections (internal/process/task_queues.js:85:21)"
I'm certainly no expert at this, but these two lines seem to be a potential issue? Isn't the Reduce function to be used on an array and it returns a value? So running Reduce on the results of another Reduce shouldn't work? In the below, stats_all doesn't become a singular value? So is it even possible to run stats_all.reduce? const stats_all = stats_core.reduce((all, one) => [...all, one, `m_${one}`, `m_${one}_mod`], []); const stat_changes = stats_all.reduce((changes, stat) => `${changes} ${stat}`, '').slice(0,-1).toLowercase();
Here's the current code. The sheet has some non-pertinent sections removed so it fit on PasteBin. Sorry for the delay! HTML: <a href="https://pastebin.com/fdShF95B" rel="nofollow">https://pastebin.com/fdShF95B</a> CSS: <a href="https://pastebin.com/ehvjDycG" rel="nofollow">https://pastebin.com/ehvjDycG</a>
1658093669

Edited 1658094062
GiGs
Pro
Sheet Author
API Scripter
John B. said: I'm certainly no expert at this, but these two lines seem to be a potential issue? Isn't the Reduce function to be used on an array and it returns a value? So running Reduce on the results of another Reduce shouldn't work? In the below, stats_all doesn't become a singular value? So is it even possible to run stats_all.reduce? const stats_all = stats_core.reduce((all, one) =&gt; [...all, one, `m_${one}`, `m_${one}_mod`], []); const stat_changes = stats_all.reduce((changes, stat) =&gt; `${changes} ${stat}`, '').slice(0,-1).toLowercase(); Reduce does return a value, but that value can be an object or array, or a string. The first reduce generates an array, the second a string. Slice works on strings, so the above code should work, and it does for me. But the error does point to that line, so something might be going on (it might be pointing at a different error - error reports aren't always telling you exactly where the error is). You can change that line to: const stat_changes = stats_all.reduce((changes, stat) =&gt; `${changes} ${stat.toLowerCase()}`, ''); This will produce the same text (except it has a space at the end, but that doesnt affect anything0. Try that and see if the error goes away, or changes to something else. Edit: ah, correction. There is likely an issue with slice when stats_all generates a 0-length array (and therefore the string is also 0 length). I'll have to fix that in my next version. The suggested change will work fine for those cases.
1658094180

Edited 1658094307
Dave
Pro
I've been just tinkering, and the beginning of my sheetworkers section now looks like this. It seems like it might all be working properly, now, but we're still testing it. const repeatingSum = (destinations, section, fields) =&gt; { if (!Array.isArray(destinations)) destinations = [destinations.replace(/\s/g, '').split(',')]; if (!Array.isArray(fields)) fields = [fields.replace(/\s/g, '').split(',')]; getSectionIDs(`repeating_${section}`, idArray =&gt; { const attrArray = idArray.reduce((m, id) =&gt; [...m, ...(fields.map(field =&gt; `repeating_${section}_${id}_${field}`))], []); getAttrs([...attrArray], v =&gt; { const getValue = (section, id, field) =&gt; v[`repeating_${section}_${id}_${field}`] === 'on' ? 1 : parseFloat(v[`repeating_${section}_${id}_${field}`]) || 0; const commonMultipliers = (fields.length &lt;= destinations.length) ? [] : fields.splice(destinations.length, fields.length - destinations.length); const output = {}; destinations.forEach((destination, index) =&gt; { output[destination] = idArray.reduce((total, id) =&gt; total + getValue(section, id, fields[index]) * commonMultipliers.reduce((subtotal, mult) =&gt; subtotal * getValue(section, id, mult), 1), 0); }); setAttrs(output); }); }); }; const sections = ['specials', 'arts', 'spaths', 'ppaths', 'dlores', 'misc', 'rituals', 'gear']; const stats_core = ['Strength', 'Dexterity', 'Charisma', 'Intelligence', 'Manipulation', 'Wits', 'Stamina', 'Composure', 'Resolve']; const stats_all = stats_core.reduce((all, one) =&gt; [...all, one, `m_${one}`, `m_${one}_mod`], []); const stat_changes = stats_all.reduce((changes, stat) =&gt; `change:${stat} ${changes}`, '').slice(0,-1).toLowerCase(); stats_core.forEach(function (stat) { on(`change:${stat}_base change:${stat}_bonus`, function () { getAttrs([`${stat}_base`, `${stat}_bonus`], function (values) { const stat_base = parseInt(values[`${stat}_base`])||0; const stat_bonus = parseInt(values[`${stat}_bonus`])||0; const final_total = stat_base + stat_bonus; setAttrs({ [`${stat}`]: final_total }); }); }); on(`change:m_${stat}_base change:m_${stat}_bonus`, function () { getAttrs([`m_${stat}_base`, `m_${stat}_bonus`], function (values) { const stat_base = parseInt(values[`m_${stat}_base`])||0; const stat_bonus = parseInt(values[`m_${stat}_bonus`])||0; const final_total = stat_base + stat_bonus; const stat_mod = (Math.floor((final_total * (final_total - 1))/2) + 1) * Math.ceil(final_total/15); setAttrs({ [`m_${stat}`]: final_total, [`m_${stat}_mod`]: stat_mod }); }); }); }); sections.forEach(section =&gt; { const special_attribute_name_string = 'attribute_name'; on(`${stat_changes} change:repeating_${section}:${special_attribute_name_string}`, () =&gt; { getSectionIDs(`repeating_${section}`, id_array =&gt; { const fieldnames = id_array.reduce((all, id) =&gt; [...all, `repeating_${section}_${id}_${special_attribute_name_string}`], []); getAttrs([...fieldnames, ...stats_all], values =&gt; { const output = {}; id_array.forEach(id =&gt; { const special_attribute_name = values[`repeating_${section}_${id}_${special_attribute_name_string}`]; output[`repeating_${section}_${id}_attribute_${section}`] = +values[special_attribute_name] || 0; output[`repeating_${section}_${id}_mattribute_${section}`] = +values[`m_${special_attribute_name}`] || 0; output[`repeating_${section}_${id}_mattribute_mod_${section}`] = +values[`m_${special_attribute_name}_mod`] || 0; }); setAttrs(output); }); }); }); }); Edit to add: the error message in the API console has also stopped appearing.
1658095306

Edited 1658101903
GiGs
Pro
Sheet Author
API Scripter
As per John's observation and my previous post, I'd recommend changing the stat_changes line from this: const stat_changes = stats_all.reduce((changes, stat) =&gt; `change:${stat} ${changes}`, '').slice(0,-1).toLowerCase(); to this, to stop it causing errors in the future: const stat_changes = stats_all.reduce((changes, stat) =&gt; `${changes}${changes.length ? ' ' : ''}${stat.toLowerCase()}`, ''); Edit: just updated it to get rid of the leading space.
1658123567

Edited 1658123581
Dave
Pro
GiGs said: As per John's observation and my previous post, I'd recommend changing the stat_changes line from this: const stat_changes = stats_all.reduce((changes, stat) =&gt; `change:${stat} ${changes}`, '').slice(0,-1).toLowerCase(); to this, to stop it causing errors in the future: const stat_changes = stats_all.reduce((changes, stat) =&gt; `${changes}${changes.length ? ' ' : ''}${stat.toLowerCase()}`, ''); Edit: just updated it to get rid of the leading space. Just for my own understanding, what's that line doing? I haven't updated it to include the leading space part, but everything seems to be working fine, now!
1658134191

Edited 1658134319
GiGs
Pro
Sheet Author
API Scripter
You know at the start of a sheet worker you normally have something like: on('change:strength change:m_strength', function() { For the sheet workers created here, it would be a pain to manually enter all the attributes. So this takes an array of attributes (like ['strength', 'm_strength', 'dexterity'] ) an creates a string variable containing&nbsp; 'change:strength change:m_strength change:dexterity ' It'll handle an array of any length, so you can supply it one attribute, or 30 attributes, and it will generate a valid string. You can then just place it after on( , without having to manually enter all those attributes. It also coerces the attributes into lower case, which you need for the event line. It builds its string from the stats_all array. the stats_all array itself has built an array of attributes from the core set - you feed it ['strength', 'dexterity',] etc, and it expends the arraym adding the m_ATTRIBUTE and m_ATTRIBUTE_mod versions. So those two lines together take your core attributes, and generate a string that works in the event line ( on() ) . The stats_all function has another purpose - since it has all the attributes, it can be used in getAttrs.&nbsp; The problem is: my first version of stat_changes always had a space at the end, and for neatness I added the slice function to remove the final character of the created string. However, my theory is that sometimes Roll20 tries to run that function with an empty length array (I'm not sure why - perhaps when a character sheet is first loaded, it might sometimes be initialised this way? It shouldnt happen...), and if that happens the string is '' - it has a length of 0 characters. When slice tries to remove the final character, there isn't one, so it crashes. The new code doesn't use slice to remove a character, so it works fine when the code generates a zero length string.
Well, I've now implemented all these changes and sheetworkers, and the sheet seems to be working beautifully! Thanks again so much for all your help. :)
1658885974

Edited 1658888137
Dave
Pro
Edit: All that over a single misplaced quote mark. Yikes. I'm going to keep this up, as I think I have a follow up question to add shortly... I'm back at it again! I'm trying to adapt the things shown to me here to a new purpose: selecting skill bonuses based on choosing a skill name, much as previously I was doing the same with m_attributes. I haven't been able to get it to work, but I thought someone might be able to spot where I'm going wrong. Here's what I've got right now: Given the inputs for these attributes: [SKILL], [SKILL]bonusdice, and roll[SKILL]bonus (for example: academics, academicsbonusdice, and rollacademicsbonus), input via the following HTML: &lt;select class="sheet-skillselect" name="attr_academics" title="academics"&gt; &lt;option value="0"&gt;--&lt;/option&gt; &lt;option value="1"&gt;1&lt;/option&gt; &lt;option value="2"&gt;2&lt;/option&gt; &lt;option value="3"&gt;3&lt;/option&gt; &lt;option value="4"&gt;4&lt;/option&gt; &lt;option value="5"&gt;5&lt;/option&gt; &lt;option value="6"&gt;6&lt;/option&gt; &lt;option value="7"&gt;7&lt;/option&gt; &lt;option value="8"&gt;8&lt;/option&gt; &lt;/select&gt; &lt;input type="number" name="attr_academicsbonusdice" title="academicsbonusdice" class="sheet-skill-input" value="0"&gt; &lt;input type="number" name="attr_rollacademicsbonus" title="rollacademicsbonus" class="sheet-skill-input" value="0" &gt; And attempting to set the attributes [SKILL]_total_dice and [SKILL]_total_bonus (for example, academics_total_dice and academics_total_bonus), as set via the following HTML: &lt;input type="number" class="sheet-skill-total" name="attr_academics_total_dice" title="academics_total_dice" value="0" readonly /&gt; &lt;input type="number" class="sheet-skill-total" name="attr_academics_total_Bonus" title="academics_total_bonus" value="0" readonly /&gt; I attempted to adapt the sheetworker you showed me like so: const skills_core = ['academics', 'animalken', 'arts', 'athletics', 'awareness', 'bureaucracy', 'cosmology', 'esoterica', 'etiquette', 'focus', 'investigation', 'larceny', 'marksmanship', 'martialarts', 'medicine', 'occult', 'piloting', 'psionics', 'rapport', 'science', 'stealth', 'survival', 'technology']; const skills_all = skills_core.reduce((all, one) =&gt; [...all, one, `${one}bonusdice`, `roll${one}bonus`], []); const skill_changes = skills_all.reduce((changes, skill) =&gt; `${changes}${changes.length ? ' ' : ''}${skill.toLowerCase()}`, ''); skills_core.forEach(function (skill) { on(`change:${skill} change:${skill}bonusdice` change:roll${skill}bonus`, function () { getAttrs([`${skill}`, `${skill}bonusdice`, `roll${skill}bonus`], function (values) { const skill_base_dice = parseInt(values[`${skill}`])||0; const skill_bonus_dice = parseInt(values[`${skill}bonusdice`])||0; const final_total = skill_base_dice + skill_bonus_dice; const skill_bonus = parseInt(values[`roll${skill}bonus`])||0; setAttrs({ [`${skill}_total_dice`]: final_total, [`${skill}_total_bonus`]: skill_bonus }); }); }); }); Edit: This works! This does not work. [SKILL]_total_dice and [SKILL]_total_bonus remain set at 0. I'm wondering if anyone can spot where I might be going wrong in my attempt to adapt the sheetworker? I'm especially unclear on what the const lines at the start of the worker do (const skills_all,const skill_changes), so that part was especially a stab in the dark. I'm going to keep tinkering with this, but I figured it was worth putting this out there! Thanks!
1658888636

Edited 1658894050
Dave
Pro
Continued from above. This is where it breaks down. This part is supposed to take the name of an ability and then reference the values stored from the above sheetworker, based on that name, and select the values stored for those ability dice/bonuses. Edit: This works when I set ability_name, but it doesn't trigger when I set [SKILL]bonusdice, and roll[SKILL]bonus, so I think my "on:X" statements must be off? I'm getting close, but this part's still eluding me. Given this input for ability_name: &lt;select name="attr_ability_name" style="width:110px; height:30px"&gt; &lt;option value="0"&gt;--&lt;/option&gt; &lt;option value="academics"&gt;Academics&lt;/option&gt; &lt;option value="animalken"&gt;Animal Ken&lt;/option&gt; &lt;option value="arts"&gt;Arts&lt;/option&gt; &lt;option value="athletics"&gt;Athletics&lt;/option&gt; &lt;option value="awareness"&gt;Awareness&lt;/option&gt; &lt;option value="bureaucracy"&gt;Bureaucracy&lt;/option&gt; &lt;option value="cosmology"&gt;Cosmology&lt;/option&gt; &lt;option value="esoterica"&gt;Esoterica&lt;/option&gt; &lt;option value="etiquette"&gt;Etiquette&lt;/option&gt; &lt;option value="focus"&gt;Focus&lt;/option&gt; &lt;option value="investigation"&gt;Investigation&lt;/option&gt; &lt;option value="larceny"&gt;Larceny&lt;/option&gt; &lt;option value="marksmanship"&gt;Marksmanship&lt;/option&gt; &lt;option value="martialarts"&gt;Martial Arts&lt;/option&gt; &lt;option value="medicine"&gt;Medicine&lt;/option&gt; &lt;option value="occult"&gt;Occult&lt;/option&gt; &lt;option value="piloting"&gt;Piloting&lt;/option&gt; &lt;option value="psionics"&gt;Psionics&lt;/option&gt; &lt;option value="rapport"&gt;Rapport&lt;/option&gt; &lt;option value="science"&gt;Science&lt;/option&gt; &lt;option value="stealth"&gt;Stealth&lt;/option&gt; &lt;option value="survival"&gt;Survival&lt;/option&gt; &lt;option value="technology"&gt;Technology&lt;/option&gt; &lt;/select&gt; I am trying to take the value selected with ability_name to assign the following attributes, based on the sheetworker from my previous post: &lt;input type="number" name="attr_ability_dice_specials" value="0" readonly/&gt; &lt;input type="number" name="attr_ability_bonus_specials" value="0" readonly/&gt; This is the attempt at adapting the sheetworker to do so: const skills_all2 = skills_core.reduce((all, one) =&gt; [...all, one, `${one}_total_dice`, `${one}_total_bonus`], []); sections.forEach(section =&gt; { const special_ability_name_string = 'ability_name'; on(`${skill_changes} change:${special_ability_name_string}bonusdice change:roll${special_ability_name_string}bonus change:repeating_${section}:${special_ability_name_string}`, () =&gt; { getSectionIDs(`repeating_${section}`, id_array =&gt; { const fieldnames = id_array.reduce((all, id) =&gt; [...all, `repeating_${section}_${id}_${special_ability_name_string}`], []); getAttrs([...fieldnames, ...skills_all2], values =&gt; { const output = {}; id_array.forEach(id =&gt; { const special_ability_name = values[`repeating_${section}_${id}_${special_ability_name_string}`]; output[`repeating_${section}_${id}_ability_${section}`] = +values[special_ability_name] || 0; output[`repeating_${section}_${id}_ability_dice_${section}`] = +values[`${special_ability_name}_total_dice`] || 0; output[`repeating_${section}_${id}_ability_bonus_${section}`] = +values[`${special_ability_name}_total_bonus`] || 0; }); setAttrs(output); }); }); }); }); So, the code from the previous post correctly calculates the values I need to reference, but I'm doing something wrong in this second sheetworker, as it's not pulling those values based on ability_name. If anyone spots what I've got wrong, I'd be very grateful! Thanks!
1658893992

Edited 1658894077
GiGs
Pro
Sheet Author
API Scripter
It's difficult to see without a more detailed explanation of what you are doing here and the full html, so we can see the other parts that are relavant. But the thing I notice: you mention these two attributes: &lt;input type="number" name="attr_ability_dice_specials" value="0" readonly/&gt; &lt;input type="number" name="attr_ability_bonus_specials" value="0" readonly/&gt; But ability_dice_specials and ability_bonus_specials aren't in the sheet worker. (I guess they are being constructed in the output[ ] section) You need to at the very least show the entire repeating section, and pick one or two of the core_stats outside the html, and post their html. Also are you getting an error message in the console, and what is it?
Thanks for the reply! I actually got that part working! Now it's just the "on:.." part that doesn't seem to be quite working. (I edited my post, above.) GiGs said: It's difficult to see without a more detailed explanation of what you are doing here and the full html, so we can see the other parts that are relavant. But the thing I notice: you mention these two attributes: &lt;input type="number" name="attr_ability_dice_specials" value="0" readonly/&gt; &lt;input type="number" name="attr_ability_bonus_specials" value="0" readonly/&gt; But ability_dice_specials and ability_bonus_specials aren't in the sheet worker. You need to at the very least show the entire repeating section, and pick one or two of the core_stats outside the html, and post their html.
I think I have it figured out. Upon investigating this further, I've found that both sets of sheetworkers shared the same problem: they did not update when the original values for attributes/abilities were updated, only when the attribute name was selected. I think I have a solution, if a slightly messy one. The statements didn't seem to work--specifically, where it calls "${stat_changes}" and "${skill_changes}," such as: on(`${stat_changes} change:repeating_${section}:${special_attribute_name_string}`, () =&gt; { I made a slightly clunky edit, but now it seems to work correctly: on(`${skill_changes} sheet:opened change:academicsbonusdice change:rollacademicsbonus change:animalkenbonusdice change:rollanimalkenbonus change:artsbonusdice change:rollartsbonus change:athleticsbonusdice change:rollathleticsbonus change:awarenessbonusdice change:rollawarenessbonus change:bureaucracybonusdice change:rollbureaucracybonus change:cosmologybonusdice change:rollcosmologybonus change:esotericabonusdice change:rollesotericabonus change:etiquettebonusdice change:rolletiquettebonus change:focusbonusdice change:rollfocusbonus change:investigationbonusdice change:rollinvestigationbonus change:larcenybonusdice change:rolllarcenybonus change:marksmanshipbonusdice change:rollmarksmanshipbonus change:martialartsbonusdice change:rollmartialartsbonus change:medicinebonusdice change:rollmedicinebonus change:occultbonusdice change:rolloccultbonus change:pilotingbonusdice change:rollpilotingbonus change:psionicsbonusdice change:rollpsionicsbonus change:rapportbonusdice change:rollrapportbonus change:sciencebonusdice change:rollsciencebonus change:stealthbonusdice change:rollstealthbonus change:survivalbonusdice change:rollsurvivalbonus change:technologybonusdice change:rolltechnologybonus change:repeating_${section}:${special_ability_name_string}`, () =&gt; { on(`${stat_changes} change:strength_base change:strength_bonus change:m_strength_base change:m_strength_bonus change:charisma_base change:charisma_bonus change:m_charisma_base change:m_charisma_bonus change:intelligence_base change:intelligence_bonus change:m_intelligence_base change:m_intelligence_bonus change:dexterity_base change:dexterity_bonus change:m_dexterity_base change:m_dexterity_bonus change:manipulation_base change:manipulation_bonus change:m_manipulation_base change:m_manipulation_bonus change:wits_base change:wits_bonus change:m_wits_base change:m_wits_bonus change:stamina_base change:stamina_bonus change:m_stamina_base change:m_stamina_bonus change:composure_base change:composure_bonus change:m_composure_base change:m_composure_bonus change:resolve_base change:resolve_bonus change:m_resolve_base change:m_resolve_bonus change:repeating_${section}:${special_attribute_name_string}`, () =&gt; { I can certainly upload all the code again, but it does appear to be working now. :)
1658904740

Edited 1658904756
GiGs
Pro
Sheet Author
API Scripter
That's because you havent replicated part of the earlier script. See here: const skills_all2 = skills_core.reduce((all, one) =&gt; [...all, one, `${one}_total_dice`, `${one}_total_bonus`], []); sections.forEach(section =&gt; { const special_ability_name_string = 'ability_name'; on(`${skill_changes} You should have a new skill_changes in there (with a new name to match the new stats), as you have changed skills_all2. A line like this should do the trick const skill_changes2 = skills_all2.reduce((changes, skill) =&gt; `${changes}${changes.length ? ' ' : ''}${skill.toLowerCase()}`, '');
That still doesn't seem to do it, oddly, but when I specifically add the skills in (as noted above) it works fine. Very odd...but hey, it works!