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

[sheet development] Creating a settings tab for handling optional rules

1603538302
Onirim
Sheet Author
Hi coders, I work actually on a character sheet compatible with many editions of Rolemaster (RM1, RM2, RMC, RMX and RMU). For achieving this, I keep the auto calculations to the minimum, but there are some of them to help managing the levelling process. My problem is certain calculations need to be different between some versions or when using one of the optional (and frequently used) rules. If I know how to make informations appear or disappear (I already use a tab system), I don't know if I can make a radio button for changing a calculation in an input. For example, the hp_max input is calculated automatically in the input field like hp_base + hp_base * (mod_con / 100), rounded. But I want to have a setting for being able to disactivate the auto-calculation for some optional rules or edition.  It is possible? Do you know a character sheet who use a system like this?  Best regards,
1603542850
GiGs
Pro
Sheet Author
API Scripter
If you use sheet workers to do that calculations, this is very simple. You just set a checkbox on your settings tab, like <input type="checkbox" name="attr_hp_calc_on" value="1" > Then in the sheet worker, you get the stats you want to work with, and make sure you grab the hp_calc_on attribute. Then you can just do if (!hp_calc_on) return; That means the sheet will stop the sheet worker before it does any calculations, and will leave that alone. The complete sheet worker would look  something like  this (some differences might be needed depending on how your sheet is built): on('change:hp_base change:mod_con change:hp_calc_on', () => {     getAttrs(['hp_base','mod_con','hp_calc_on'], values => {         const base = parseInt( values.hp_base) || 0;         const mod = parseInt( values.mod_con) || 0;         const check = parseInt( values.hp_calc_on) || 0;         // check to see if calculation is on or off. If off, end the sheet worker.         if(!check) return;         // calculate hp         const hp = Math.round(base * (1+ (mod / 100)));         // update hp attribute on sheet.         setAttrs({             hp: hp         });     }); }); If you're use autocalc fields instead, its a fair bit trickier and I really don't recommend it.
1603545702
Onirim
Sheet Author
Thank you very much, it's working with HP, but I want to calc the max HP value of an attribute, and the Sheet Worker doesn't seems to recognize hp_max as a variable. How can we put a value on the max attribute instead on the actual value of an attribute ? :)
1603546114
Andreas J.
Forum Champion
Sheet Author
Translator
Onirim said: Thank you very much, it's working with HP, but I want to calc the max HP value of an attribute, and the Sheet Worker doesn't seems to recognize hp_max as a variable. How can we put a value on the max attribute instead on the actual value of an attribute ? :) Weird, nothing in the Sheetworker documentation indicates there being anything special about the "_max" value. Please show us your code to see what's going on. Did you manage to make "hp" work at least, even if "hp_max" didn't seem to be recognized?
1603546592
Onirim
Sheet Author
It's working with hp value, yes! And this is so strange. There are my inputs: <label data-i18n="totalhp" style="width: 63px;">PdC total</label><input type="number" name="attr_hp_max" class='sheet-short'/> <label data-i18n="actualhp">PdC actuels</label><input type="number" name="attr_hp" style="width: 70px;"/> And the Sheet Worker on('change:hp_base change:base_constitution change:race_constitution change:totalhpcalculation', () => { getAttrs(['hp_base','base_constitution','race_constitution','totalhpcalculation'], values => { const base = parseInt( values.hp_base) || 0; const basemod = parseInt( values.base_constitution) || 0; const racemod = parseInt( values.race_constitution) || 0; const check = parseInt( values.totalhpcalculation) || 0; // check to see if calculation is on or off. If off, end the sheet worker. if(!check) return; // calculate hp const hp = Math.round(base * (1+ ((basemod + racemod) / 100))); // update hp attribute on sheet. setAttrs({ hp: hp_max }); }); }); And the error:  ReferenceError: hp_max is not defined     at Object.eval Thank you for the help :)
1603546767

Edited 1603546879
GiGs
Pro
Sheet Author
API Scripter
You have the setattrs reversed. It should be                         setAttrs({ hp_max: hp }); If you want to set hp and hp_max, you can do both at the same time                         setAttrs({ hp_max: hp,                                 hp: hp });
1603548967
Andreas J.
Forum Champion
Sheet Author
Translator
GiGs said: You have the setattrs reversed. It should be                         setAttrs({ hp_max: hp }); If you want to set hp and hp_max, you can do both at the same time                         setAttrs({ hp_max: hp,                                 hp: hp }); Huh, I wasn't even aware that attributes and their "_max" value are mapped like that in sheetworkers. Don't think I've ever manipulated "_max" value in any self-crafted sheetworkers.
1603549337

Edited 1603549462
Onirim
Sheet Author
That's work fine now, thank you very much :) Sorry for the dumb error ^^ Now I need to plug this with the useroption of the sheet.json. I've made a  "useroptions":  [ { "attribute": "totalhpcalculation", "displayname": "Type of Total HP calculations: ", "type": "select", "options": [ "Legacy calculation|1", "No calculation|0" ], "default": "1", "description": "Choose the Total HP calculation. In Legacy, the Total HP equals the Base HP + Base HP * (constitution bonus / 100). If you choose No Calculations, the Total HP calculation is not calculated and you will need to enter the value manually." } ] Not really working, changing the option don't change anything in my attr_totalhpcalculation attribute and it not create this attribute. Sadly I prefer to handle some options by the sheet.json, and some other options by a tab on the character sheet. I need to add somethink in the html to handle this change ?
1603551737
Andreas J.
Forum Champion
Sheet Author
Translator
Those options in the Sheet.json, when they exist on the Default Settings section of a campagin, only affects new characters created after the settings are changed.  If you go in-game to the Settings tab, and scroll to the bottom where the Experimental Features section is, there is a button that says Apply Default Settings, that should apply the settings to all character sheets. Unsure how well the last part can be tested in a Sheet Sandbox, but generally those things are only available when the sheet exist in the dropdown menu, as you can add sheet.json in your custom games.
1603624611

Edited 1603625261
Onirim
Sheet Author
Thank you very much, all your advice are really nice, guys :) I've another one to ask: I want to do the same autocalculation switch but with items on a repeating list. I imagine I can't use the attribute name like for a regular attribute, so how it can be accomplished ? For example, I've a repeating list with skills and by default I calculate a total professional bonus by multiplying a professional bonus by the level of the character (because in legacy Rolemaster, professional bonus is given by level). But in some options and versions use a flat bonus, so I need to disactivate the multiplication. I now see how to do this, but I don't see how I can call an repeating list attribute. Any advice for this ? [EDIT] I've found the wiki pages for this, so I'll read all of them before asking, sorry !
1603626372
GiGs
Pro
Sheet Author
API Scripter
If you're looking at the wiki, you'll need to look at the getSectionIDs function. 
1603628056
Onirim
Sheet Author
So, I've found how to calculate the professional bonus when I modify the prof_skillname value in my repearing_skills list. It work as intended when I modify a value inside the repeating list.  But when I modify the level value (who is not in the repeating list) or the switch value named professionbonuscalcmode, I've an error because the Sheet Worker need to know what line I want to calc. And I want to calculate all the profbonus_skillname in my entire repeating list :) There is a way do ask the Sheet Worker to apply to all lines on the repeating list ? on('change:repeating_skills change:professionbonuscalcmode change:niveau', () => { getAttrs(['repeating_skills_prof_skillname','niveau', 'professionbonuscalcmode'], values => { const prof = parseInt( values.repeating_skills_prof_skillname) || 0; const level = parseInt( values.niveau) || 0; const check = parseInt( values.professionbonuscalcmode) || 0; // check to see if calculation is on or off. If off, end the sheet worker. if(check==0) { const profbonus = prof*level; setAttrs({ repeating_skills_profbonus_skillname: profbonus }); } else if(check==1) { const profbonus = prof; setAttrs({ repeating_skills_profbonus_skillname: profbonus }); } else return; }); });
1603630493
GiGs
Pro
Sheet Author
API Scripter
This is where the getSectionIDs function comes in. There's a couple of odd things about your sheet worker. Is professionbonuscalcmode a checkbox? If so it can only have a value of 1 or 0; but you have a third branch on your if(check) if statement. More importantly, you have a potentially big problem with the  repeating_skills_prof_skillname  attribute. You have it in the change event line, and also in the setAttr line. This means you have an infinite loop here: when this line runs: const profbonus = prof*level; the value will change. That will trigger the on(change) line, and the sheet worker will run again - then the above line will run again, then the new value will be saved, then the event will trigger again, and the sheet worker will run again... This will run until the sheet worker crashes, and if level is above 1, the profbonus will skyrocket up to infinity. You cant have an attribute update itself in this way. With that in mind, I present a sheet worker modified from your last post that handles multiple rows. IMPORTANT:  this is not the sheet worker to use. It has the critical error of the infinite loop I just described. I am posting it just to show you the structure. I don't know what exactly is meant to happen here and don't know your sheet, so I dont have the information needed to suggest a correction to fix the error. on ( 'change:repeating_skills:prof_skillname change:professionbonuscalcmode change:niveau' , ()  =>  {      getSectionIDs ( 'repeating_skills' ,  ids   =>  {          // get an array of anmes for the skillname attribute across all rows.          // getSectionIDs gives an array of the row ids.          const   fieldnames  = [];          ids . forEach ( id   =>   fieldnames . push ( `repeating_skills_ ${ id } _prof_skillname` ));          // now getAttrs can use that array to get the attributes from all rows          getAttrs ([... fieldnames , 'niveau' ,  'professionbonuscalcmode' ],  values   =>  {              // get the values of the attributes outside the repeating section              const   level  =  parseInt (  values . niveau ) ||  0 ;              const   check  =  parseInt (  values . professionbonuscalcmode ) ||  0 ;                           // create a variable to hold all the stats that need to be updated              const   output  = {};              // now loop through the row ids again              ids . forEach ( id   =>  {                  // get the value of the prof_skillname attribute for this specific row.                  const   prof  =  parseInt (  values [ `repeating_skills_ ${ id } _prof_skillname` ]) ||  0 ;                                   // check to see if calculation is 1 or 0. Use triple equality to match properly.                  if ( check  ===  0 ) {                      const   profbonus  =  prof * level ;                      output [ `repeating_skills_profbonus_skillname: profbonus` ] =  profbonus ;                 }  else   if ( check  ===  1 ) {                      const   profbonus  =  prof ;                      output [ `repeating_skills_profbonus_skillname: profbonus` ] =  profbonus ;                 }             });              // save the collected attributes.              setAttrs ( output );         });     });     });
1603641492

Edited 1603644207
Onirim
Sheet Author
professionbonuscalcmode  is not a checkbox, the values can be 0 or 1 for the moment, but I can add other calculations method in the future, so I can add others values. For the loop thing, I don't have a loop when I use the Sheet Worker. I think the value is calculated only 1 time because there is only 1 possible result, so the if a loop appear, the value don't be changed more than 1 time (and is calculated 2 times yes). Or maybe the browser handle this sort of loop? Anyway, I don't see any warning about that in the browser console so... for the moment, no loop! (but really I will test in many ways). It work with this code :  on('change:repeating_skills:prof_skillname change:professionbonuscalcmode change:niveau', () => {     getSectionIDs('repeating_skills', ids => {         // get an array of names for the skillname attribute across all rows.         // getSectionIDs gives an array of the row ids.         const fieldnames = [];         ids.forEach(id => fieldnames.push(`repeating_skills_${id}_prof_skillname`));         // now getAttrs can use that array to get the attributes from all rows         getAttrs([...fieldnames,'niveau', 'professionbonuscalcmode'], values => {             // get the values of the attributes outside the repeating section             const level = parseInt( values.niveau) || 0;             const check = parseInt( values.professionbonuscalcmode) || 0;                          // create a variable to hold all the stats that need to be updated             const output = {};             // now loop through the row ids again             ids.forEach(id => {                 // get the value of the prof_skillname attribute for this specific row.                 const prof = parseInt( values[`repeating_skills_${id}_prof_skillname`]) || 0;                                  // check to see if calculation is 1 or 0. Use triple equality to match properly.                 if(check === 0) {                     const profbonus = prof*level;                     output[`repeating_skills_${id}_profbonus_skillname`] = profbonus;                 } else if(check === 1) {                     const profbonus = prof;                     output[`repeating_skills_${id}_profbonus_skillname`] = profbonus;                 }             });             // save the collected attributes.             setAttrs(output);         });     });     }); I'm a happy man! I will ask you again if I'm in trouble with the sheet :) Regards,
1604257912
Onirim
Sheet Author
Hi there! I've learn so much about Sheet Workers, so now I can do the major changes I need. But I'm stuck with an option for changing the background style color of my character sheet. I've added a Sheet Worker for this and the code in the CSS file, but the background color doesn't change, like the CSS doesn't take the value of my attribute. On the HTML page I've added this : &lt;input class="sheet-mainBgColor" name="attr_mainBgColor" type="hidden" value="default"/&gt; There is my option butter (inspired by the Ryuutama code for the moment, but I will change that once I get it work) &lt;label data-i18n="background-color" style="width: 200px;"&gt;Background color&lt;/label&gt;: &lt;select name="attr_mainBgColorSelect" value="default"&gt; &lt;option value="default" data-i18n="default" style="background-color: #eeffff;" selected="selected"&gt;Default&lt;/option&gt; &lt;option value="white" data-i18n="white" style="background-color: #fff;"&gt;White&lt;/option&gt; &lt;option value="lilac" data-i18n="lilac" style="background-color: #fbeeff;"&gt;Lilac&lt;/option&gt; &lt;option value="grayscale" data-i18n="grayscale" style="background-color: #f3f3f3;"&gt;Grayscale&lt;/option&gt; &lt;option value="book" data-i18n="book" style="background-color: #f3efe6;"&gt;Book&lt;/option&gt; &lt;option value="summer" data-i18n="season-summer" style="background-color: #fef7ed;"&gt;Summer&lt;/option&gt; &lt;option value="fall" data-i18n="season-fall" style="background-color: #fde7e8;"&gt;Fall&lt;/option&gt; &lt;option value="winter" data-i18n="season-winter" style="background-color: #fafefe;"&gt;Winter&lt;/option&gt; &lt;option value="spring" data-i18n="season-spring" style="background-color: #f8f9f3;"&gt;Spring&lt;/option&gt; &lt;/select&gt; &lt;p data-i18n="backgroundcolordesc"&gt;Choose the main color of the character sheet (changes background and headers).&lt;/p&gt; On the HTML Sheet Worker section I've added this :&nbsp; on("change:mainBgColorSelect", function(event) { getAttrs(["mainBgColorSelect", "mainBgColor"], function(values) { setAttrs({ mainBgColor: values.mainBgColorSelect }); }); }); And in my CSS code, I've added this: .charsheet { background-color: var(--main-bg-color); width: 1000px; } .charsheet { --main-bg-color: #eeffff; --header-bg-color: #aaeeaa; --divider-color: lightgray; --input-bg-color: #fff; } .sheet-mainBgColor[value="white"] ~ .sheet-characterSheet { --main-bg-color: #fff; --divider-color: #90939c; } .sheet-mainBgColor[value="grayscale"] ~ .sheet-characterSheet { --main-bg-color: #f3f3f3; --divider-color: #000; } .sheet-mainBgColor[value="book"] ~ .sheet-characterSheet { --main-bg-color: #f3efe6; --divider-color: #424242; } .sheet-mainBgColor[value="summer"] ~ .sheet-characterSheet { --main-bg-color: #fef7ed; --divider-color: #424242; } .sheet-mainBgColor[value="fall"] ~ .sheet-characterSheet { --main-bg-color: #fde7e8; --divider-color: #cd5142; } .sheet-mainBgColor[value="winter"] ~ .sheet-characterSheet { --main-bg-color: #fafefe; } .sheet-mainBgColor[value="spring"] ~ .sheet-characterSheet { --main-bg-color: #f8f9f3; --divider-color: #000; } .sheet-mainBgColor[Value="lilac"] ~ .sheet-characterSheet { --main-bg-color: #fbeeff; --divider-color: #000; } I don't really understand where is the step I miss :) Full code here : <a href="https://github.com/Onirim/Roll20-Rolemaster-2nd-classic" rel="nofollow">https://github.com/Onirim/Roll20-Rolemaster-2nd-classic</a> If a kind developer can help! Thanks :)
1604304154

Edited 1604305998
GiGs
Pro
Sheet Author
API Scripter
The issue is that&nbsp;you are applying the style directly to the .charsheet class (or are you, that sheet-characterSheet in the CSS confuses me). Remember that the input applying the style needs to be on the same "level" in the CSS hierarchy. And the charsheet class contains everything we create - we cant put anything on the same level as it. Many, many sheets create a container div to contain everything on that sheet. You can then put the style on that div and put the input before it. Like &lt;input class="sheet-mainBgColor" name="attr_mainBgColor" type="hidden" value="default"/&gt; &lt;div class="everything-else"&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- ALL the sheet code goes here --&gt; &lt;/div&gt; Then your styling would look like div.sheet-everything-else { background-color: var(--main-bg-color); width: 1000px; } div.sheet-everything-else { --main-bg-color: #eeffff; --header-bg-color: #aaeeaa; --divider-color: lightgray; --input-bg-color: #fff; } &nbsp; input.sheet-mainBgColor[value="white"] ~ div.sheet- sheet-everything-else&nbsp; { --main-bg-color: #fff; --divider-color: #90939c; } &nbsp; Obviously you can use a different class name than everything-else . Some I've used:&nbsp; .sheet- container , .sheet-wrapper ,&nbsp; .sheet- main
1604304421
GiGs
Pro
Sheet Author
API Scripter
Also, you dont need the sheet worker. Just change your select name: &lt;select name="attr_mainBgColor"&gt; &lt;option value="default" data-i18n="default" style="background-color: #eeffff;" selected &gt;Default&lt;/option&gt; The value on the start line doesnt do anything - it's the selected property that sets the default value.
1604304868
GiGs
Pro
Sheet Author
API Scripter
I would replace those styles in the option values with classes. Like &lt;option value="default" data-i18n="default" class="background-default" selected &gt;Default&lt;/option&gt; Then in the CSS have .sheet-background-default { &nbsp;&nbsp;&nbsp;&nbsp;background-color: #eeffff; } or .sheet-background-default { background-color: #eeffff; } This makes later changes to styling easier, and also allows you to reuse the style if you need to.
1604306355
GiGs
Pro
Sheet Author
API Scripter
Thanks for asking this question, btw. I'd never actually used variables in CSS before, but I just added this to a simple sheet for a homebrew I'm preparing for my group, and it is very nifty. I applied it a little differently. I have a main wrapper div, and a bunch of divs inside that. I applied the class to the inside divs, so it looks like this, when the Fall colour is selected.&nbsp;
1604307193
Andreas J.
Forum Champion
Sheet Author
Translator
GiGs said: Thanks for asking this question, btw. I'd never actually used variables in CSS before, but I just added this to a simple sheet for a homebrew I'm preparing for my group, and it is very nifty. I applied it a little differently. I have a main wrapper div, and a bunch of divs inside that. I applied the class to the inside divs, so it looks like this, when the Fall colour is selected. I've used them in limited fashion, due to not managing to get the ".charsheet" thing to apply to the whole sheet back when I did things with it. Way back when I got started with the Free Spacer sheet(maybe Christoph have continues and expanded on that later), I did some stuff with it, but ran into trouble and then probably skipped most. more recently used a bit in roll templates for my Stargate sheet. If doing that wrapper thing makes the CSS variables work anywhere inside the wrapper(regardless of depth) I'm glad, as that makes things easier.
1604307609
Andreas J.
Forum Champion
Sheet Author
Translator
This color scheme switcher setting got me thinking, we could try to distill this into an simple example for creating a light/dark mode switch for a character sheet. Having a simple example of it would be be great.
1604308989

Edited 1604312759
GiGs
Pro
Sheet Author
API Scripter
A very simple light/dark mode could like this (though the colours I've used here are just switching black and white - you'd probably want less harsh colours). This is for a checkbox that just has 2 values. The hidden input might not be necessary - you could just check for :checked value, but I prefer using hidden inputs :) CSS: div.sheet-colour&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;background-color:&nbsp;var(--main-bg-color); &nbsp;&nbsp;&nbsp;&nbsp;color:&nbsp;var(--front-color); } input[type="hidden"].sheet-colorselector[value="0"]&nbsp;&nbsp;~&nbsp;div.sheet-container&nbsp;{&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;--back-color:&nbsp;white; &nbsp;&nbsp;&nbsp;&nbsp;--front-color:&nbsp;black;&nbsp; } input[type="hidden"].sheet-colorselector[value="1"]&nbsp;&nbsp;~&nbsp;div.sheet-container {&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;--back-color:&nbsp;black; &nbsp;&nbsp;&nbsp;&nbsp;--front-color:&nbsp;white;&nbsp; } Then set up the HTML, which is really simple: &lt;input&nbsp;name="attr_backgroundcolour"&nbsp;&nbsp;&nbsp;type="checkbox"&nbsp;value="1"/&gt;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input&nbsp;class="colorselector"&nbsp;&nbsp;&nbsp;name="attr_backgroundcolour"&nbsp;&nbsp;&nbsp;type="hidden"&nbsp;value="0"/&gt;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div&nbsp;class="container"&gt; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;all&nbsp;sheet code here&nbsp;--&gt; &lt;/div&gt;
1604320301
Onirim
Sheet Author
Thank you very much for your help :) It's working!
1604326480
Andreas J.
Forum Champion
Sheet Author
Translator
Added a link to the above example on the CSS Wizardry page
1604326548
GiGs
Pro
Sheet Author
API Scripter
Onirim said: Thank you very much for your help :) It's working! I'm glad to hear it :)