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

[Info] Sheet Workers Wizardry

1453494552

Edited 1453497526
Finderski
Pro
Sheet Author
Compendium Curator
Well...my additions will be more like Cantrips than wizardry, but you get the idea. :) Also, because I'm just learning this javascript thing, if there are suggestions to make what I'm sharing here better...please share. I'd love to improve and make these things more efficient. I thought it would be good to add forum similar to the CSS but focus on Sheet Workers capabilities.  So, here are things I think should stick around in a fairly obvious place: TheAaronSheet: copy it from this  location and paste it at the top of you Sheet Workers Scripts (especially if you're going to use simple sums from repeating sections). Update one field based on the value of another: The HTML: <label class='sheet-Rank-n-XP sheet-nameRankXp'></label> <input class="sheet-standardNum" type="number" name="attr_xp" title="@{xp}"/> <select name='attr_rank' title="@{rank}" value='1' class='sheet-rank'> <option value='1'>Novice</option> <option value='2'>Seasoned</option> <option value='3'>Veteran</option> <option value='4'>Heroic</option> <option value='5'>Legendary</option> </select> The Sheet Worker: on("sheet:opened change:xp change:rank", function() { getAttrs(["xp", "rank"], function(value) { console.log("xp value: " + value.xp); console.log("rank value: " + value.rank);       if (value.xp >=80) {       console.log("Setting rank to Legendary");       setAttrs({Rank: 5});       }       else if (value.xp >=60) {       console.log("Setting rank to Heroic");       setAttrs({Rank: 4});       }       else if (value.xp >=40) {       console.log("Setting rank to Veteran");       setAttrs({Rank: 3});       }       else if (value.xp >=20) {       console.log("Setting rank to Seasoned");       setAttrs({Rank: 2});       }       else {       console.log("Setting rank to Novice");       setAttrs({Rank: 1});       }     }); }); Basically, if the Experience Points field meets a particular threshold, then the the Rank drop down box will be updated with the appropriate value. Visual Cues (or changing the background color of field based on criteria): The HTML: <div class="sheet-Strain"> <input type="text" name="attr_strain" title="@{strain}" value="4" <input type="number" name="attr_StrainMod" title="@{StrainMod}" class="sheet-standardNum" value="0" /> <input class="sheet-standardNum" type="number" name="attr_Strain_max" title="@{Strain_max}" /> <input class="sheet-danger" type="checkbox" name="attr_straindanger" value="1" /> <input class="sheet-critical" type="checkbox" name="attr_straincrit" value="1" /> <input type="number" name="attr_StrainCur" title="@{StrainCur}" class="sheet-derivedStat sheet-strain" readonly /> </div> The CSS: .charsheet .sheet-danger, .charsheet .sheet-critical { display: none; } .charsheet input.sheet-danger:checked ~ input.sheet-strain { background: #e8a400; } .charsheet input.sheet-critical:checked ~ input.sheet-strain { background: #e93a41; } The Sheet Worker Code: on("change:strain_max change:totalstrain", function() { console.log("<====== Checking for Strain Fatigue ======>"); getAttrs(["Strain_max","totalStrain"], function(cvalue) { console.log("max strain: " + cvalue.Strain_max); console.log("tot strain: " + cvalue.totalStrain); var max_Strain = parseInt(cvalue.Strain_max); var tot_Strain = parseInt(cvalue.totalStrain); console.log("maxStrain = " + typeof max_Strain); console.log("totStrain = " + typeof tot_Strain); strainzone = Math.floor(parseFloat((tot_Strain / max_Strain)) * 100); console.log("Danger Zone: " + strainzone); if (strainzone >= 100) {       console.log("At or exceeded Max Strain");       setAttrs({straincrit: 1,        straindanger: 0});       }       else if (strainzone >= 75) {       console.log("Nearing Max Strain");       setAttrs({straincrit: 0,        straindanger: 1});       }       else {       console.log("Nothing to worry about yet");       setAttrs({straincrit: 0,        straindanger: 0});       } }); }); Here I change the background color the StrainCur field based on the value of that field compared to the max of that field.  If the current is less than 75% of the max, then the field remains white; once the current value is 75-99% of the  max, the background color the field turns yellow, alerting the player that they are getting to close to their max (and dire consequences if not careful). This change is done by "checking" one of two check boxes and then using the CSS to change the color. If the current value is 100+% of the max, then the color changes red, by "checking" the other checkbox and unchecking the first. So, what else have you all used Sheet Workers for and how'd you accomplish it? :)
G V. said: So, what else have you all used Sheet Workers for and how'd you accomplish it? :) ...I've heard of sheet workers.  Does that count? Very cool G.V.  Thanks for sharing
I'm always up for a spot o' showing off, so here's something simple I cooked up over the weekend: on("change:input", function() {     infoImport(); }); var infoImport = function() {         getAttrs(["input"], function (v) {             var char = JSON.parse(v.input);             for (var property in char) {                 if (char.hasOwnProperty(property)) {                         t={};                         t[property]=char[property];                         setAttrs(t);}                         }         });     }; With this code, you can put a JSON object in a field titled input, with keys and values corresponding to attributes and values accordingly, and the sheet will be populated with that information. As for the why? This is for the Pokémon Tabletop United character sheet. There's a fairly popular Google Drive sheet for PTU that auto-fills most of the information necessary for a character. Incredibly useful for a Pokémon game, where every player can have many different characters. I went ahead and added an extra page to the automated sheet, which creates a JSON string from the character information. Copy that string, paste it in the input field, and BAM. Sheet filled out/updated lickety split. (My actual script in the Roll20 sheet is a bit more complicated, since I wanted to have repeating field functionality.)
1454061043
Finderski
Pro
Sheet Author
Compendium Curator
That's pretty cool!
1454095990
vÍnce
Pro
Sheet Author
GV claims he doesn't know js and at the same time slaps this down on the table.  ;-)   Every time I think I'm done tweaking a sheet something like this comes along and gets added to my to-do list.  Thanks.  No I mean it.  ;-P Thanks for these GV and Corin!
1454380533
Finderski
Pro
Sheet Author
Compendium Curator
Vince said: GV claims he doesn't know js and at the same time slaps this down on the table.  ;-)   LOL And now for something new... People want to roll a bunch of dice and have them displayed without summing them up. The hover over the result option is ok, but still, not optimal. OVA is a game whose mechanics can get tricky with rolling a bunch of d6's and the number varies depending on the situation. So, I decided thought if I could have a field to grab the number of dice that needed to be rolled, I could use a sheet worker to create the roll template input dynamically. HTML: <span style="margin-left: 300px; font-weight: bold;">Number of Dice to roll:</span> <input type="number" name="attr_numdicetoroll" value=1 style='border: 1px solid #801421; background-color: #eee2e2; color: black; text-align: center;' /> <input type="text" name="attr_rollformula" style="display: none;" value="{{Die one=[[1d6]]}}" /> <button class="sheet-templatebutton" type='roll' name='roll_genericroll' title="@{genericroll}" value='&{template:ova} {{name=@{character_name}}} @{rollformula}'></button> <button class="sheet-initiativebutton" type='roll' name='roll_initiative' title="@{initiative}" value='/roll @{numdicetoroll}d6 &{tracker}'>Initiative</button> Script: /* ---- START: ordinalInWord ---- */ function ordinalInWord( cardinal ) { 'use strict'; var ordinals = [ 'Zeroth ', 'First ', 'Second ', 'Third ', 'Fourth ', 'Fifth ', 'Sixth ', 'Seventh ', 'Eighth ', 'Ninth ', 'Tenth ', 'Eleventh ', 'Twelfth ', 'Thirteenth ', 'Fourteenth ', 'Fifteenth ', 'Sixteenth ','Seventeenth ', 'Eighteenth ', 'Nineteenth ', 'Twentieth ' ]; var tens = { 20: 'Twenty', 30: 'Thirty', 40: 'Forty' /* and so on up to 90 */ }; var ordinalTens = { 30: 'Thirtieth', 40: 'Fortieth', 50: 'Fiftieth' /* and so on */ }; if( cardinal <= 20 ) {                     return ordinals[ cardinal ]; } if( cardinal % 10 === 0 ) { return ordinalTens[ cardinal ]; } return tens[ cardinal - ( cardinal % 10 ) ] + ordinals[ cardinal % 10 ]; } /* ---- END: ordinalInWord ---- */ /* ---- START: Build Dice Roll for OVA Template ---- */ on("sheet:opened change:numdicetoroll", function () { 'use strict'; getAttrs(["numdicetoroll"], function (value) { console.log("Number of Dice to Roll: " + value.numdicetoroll); var dice = parseInt(value.numdicetoroll); console.log("Number of Dice Type = " + typeof dice); var newFormula = " "; var newOrd; var i = 0; for (i = 1; i <= dice; i += 1) { console.log("i = " + i); newOrd = ordinalInWord(i); newFormula += "{{" + newOrd + "Die=[[1d6]]}} "; console.log("Roll Formula = " + newFormula); } setAttrs({rollformula: newFormula}); }); }); //End Rank /* ---- END: Build Dice Roll for OVA Template ---- */ NOTE: the ordinalInWord script I stole from the internet Roll Template: <rolltemplate class="sheet-rolltemplate-ova"> <!--<h2 class="sheet-caption">{{name}}</h2>--> <div class="sheet-rtcard"> <table> <caption>{{name}}</caption> <tbody> <tr class="sheet-rth"> <th colspan="2">Roll Results</th> </tr> {{#allprops()}} <tr> <td class="sheet-rresults">{{key}}</td> <td class="sheet-rollresults" style='text-align: right;'>{{value}}</td> </tr> {{/allprops()}} </tbody> </table> </div> </rolltemplate> Output: Now, I'm just trying to figure out an easy to use this for the Mook sheet on the Savage Worlds (Tabbed) sheet...unfortunately, it's not as simple... :-/
1454387519
Wes
Sheet Author
G V. Thats sexy! I see you put the Allprops() to good use!
1454455170
vÍnce
Pro
Sheet Author
That's cool.  It will just feed the roll template adding as many rolls as needed?
1454455779
Finderski
Pro
Sheet Author
Compendium Curator
Yep. At least up to 50 (or probably 59). But filling out the rest of the 60-90 should get you up to 99 rolls.
Welcome to this little "How to custom tailor roll commands from a set of settings using Sheet Workers" Before Sheet Workers it was super annoying if you had different options leading to a different roll on the same button. There were various ways how to deal with this (API, different buttons, etc) but now you can easily stitch roll commands together by using Sheet Workers. This allows you to apply options for all buttons instead of having to duplicate functionality. As an example how I used this for my shadowrun 5 sheet: Some rolls are affected by injury modificators (e.g. (5-injurymod)d6>5) Some rolls are affected by an edge attribute. Those rolls will explode (e.g. 5d6!>5) Both options are toggleable and can be used at the same time or none can apply. The solution now: simply store the parts in hidden attributes and set those to empty strings or the value you require depending on the setting (e.g. enable injury modificators/enable use of edge). <!--We will use those attributes to stitch our roll command together --> <input type="text" name="attr_cmd_use_edge_token"/> <input type="text" name="attr_cmd_use_edge_text"/> <input type="text" name="attr_cmd_use_edge_mod"/> <input type="text" name="attr_cmd_injury_mod"/> <input type="text" name="attr_cmd_injury_text"/> <!-- This is our simple charactersheet --> <span>Edge:</span> <input type="text" name="attr_edg"/> <span>Injury Modifier:</span> <input type="text" name="attr_injury_mod"/> In this case I just use a checkbox but you can use any arbitrary attributes a SheetWorker can react on to set those settings. <!-- This is on our settings tab or wherever you want to be able to activate those checkboxes. You will likely want to style those (see CSS Wizardry) --> <input type="checkbox" name="attr_use_injury_mod"></input> <input type="checkbox" name="attr_use_edge"></input> Now you need your event listener: on("sheet:opened change:use_edge", function() { getAttrs(["use_edge"], function(v) { use_edge = (v.use_edge == "on") if(use_edge){ /* If the checkbox is set*/ setAttrs({ 'cmd_use_edge_token': "!", /*This will let our roll explode*/ 'cmd_use_edge_text' : "{{edge=Edge @{edg}}}", /* This is a template attribute only shown if edge is enabled*/ 'cmd_use_edge_mod' : "+@{edg}" /*This is a modifier for template attributes as well as dice pools */ }); }else{ /* If the checkbox is not set*/ setAttrs({ 'cmd_use_edge_token': "", /* Empty strings allow us to stitch the commands together*/ 'cmd_use_edge_text' : "", 'cmd_use_edge_mod' : "" }); } }); }); on("sheet:opened change:use_injury_mod change:injury_mod", function() { getAttrs(["use_injury_mod","injury_mod"], function(v) { use_injury = (v.use_injury_mod == "on") mod = parseInt(v.injury_mod) if(use_injury && mod > 0){ /* If there is a injury mod >0 we want to substract it from the dice pool if injury mods are enabled */ setAttrs({ 'cmd_injury_mod': "-@{injury_mod}", /* This is our dice pool modifier as well as template attribute modifier*/ 'cmd_injury_text': "{{injurymod=-@{injury_mod}}}"/*This is a custom template attribute only shown if injury mods are used*/ }); }else{ setAttrs({ 'cmd_injury_Mod': "", 'cmd_injury_text': "" }); } }); }); And all we need now is our roll button. I will use the default template to show the functionality, but obviously I used a own styled roll template. With the default template you should be able to copy/paste the code into your sheet and try it. Beware I haven't tested this simplified version :) <button type='roll' name='roll_something' value="/em &{template:default}{{mod=[[3@{cmd_use_edge_mod}@{cmd_injury_mod}]]}}{{successes=[[(?{Dicepool?}+3@{cmd_use_edge_mod}@{cmd_injury_mod})d6@{cmd_use_edge_token}sacs5cs6>5]]}}@{cmd_use_edge_text}@{cmd_injury_text}"></button> This button now rolls the number of d6 specified by the user with a constant modifier +3. It will explode the roll if the checkbox use_edge is set. If this is true it will also modify the dicepool by the number specified in the attr_edg input field. If use_injury_mod is checked it will also substract the number specified in attr_injury_mod from the dicepool. Also in both cases one additional template attribute is passed, which you can use to also build roll templates using conditions cooler and more complex than the build in roll template conditional logic.
1455559470
Dan W.
Sheet Author
Can sheet workers have memory?  I.e. if I needed to keep track of accumulated damage on a token and then apply some condition (roll to see if they are still alive based on that damage, or if they reach a bloodied condition or two examples), is that possible?   I've been away from my sheet for awhile, but want to take advantage of these new and exciting offerings  :-)  
1455560485
Finderski
Pro
Sheet Author
Compendium Curator
The memory is based on what's available in fields.  So, I believe you could, just have a watch on the HP field (via an onchange event). It wouldn't be able to pop anything up, but you could have another field that gets updated with the new state, and perhaps even change the color based on the state to draw attention to it.