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

Sheets: changing a default value for new sheets only

1659245433

Edited 1659245491
vÍnce
Pro
Sheet Author
Problem: working on existing sheet code with an attribute that defaults to "10".  Although this attribute is often changed to a different value, it's common for this value to remain "untouched" as well and retain a value of "10".  I would like to change the default value to "8" for all new sheets, but I've noticed that after changing the default to "8", existing sheets change any "untouched" version of this particular attribute from "10" to "8".  I thought maybe I could do a version check(sheetworkers)...  if this is a new sheet, do nothing. If this is an old sheet, test for the old default value. ie output.foo = (currentValue  === 10) ? 10 : currentValue .  But it seems that an "untouched" attribute is updated to "8" (per the HTML) before I can do a check with sheetworkers.  I've left the default at "10" for now, but I'm curious how one might approach this?  Thanks
1659258404
Richard W.
Pro
Sheet Author
API Scripter
Compendium Curator
Hi, I think I may have stumbled into the same issue but rather than having a default value set but I had left it without a value. Now, wanting to add an actual default setting does according to my testing exactly what you say - it will override any character sheet attribute value that has not changed it, and there is no way to actually get the value that is assigned (because it comes back as undefined). Perhaps you can set the value to 10 if the getAttrs function returns 'undefined' for that attribute, since you can assume it is just using the default value? My problem is slightly different since my attributes really are checkboxes, and without a value set to it there is no way to make changes to the default value without resetting the attribute for everyone. Basically going from just saying that a checkbox should be checked to to giving it a attribute value of 1 will reset the whole attribute for all characters, at least that is how I understood it. It gets complicated when coding oneself into a corner... In my sheets I have started to check whether a character is a new one or not, by using a variable called new_char or so that is set to 1 by default. In the version checking switch statements I check if new_char has value 1, if it does then I set it to 0 and don't need to run any upgrade functions. I guess it also means I can make any other changes  like setting variables to an actual value if needed.  The rest of my version checking I have proudly stolen from mentors in the community, mostly yourself! /Richard
1659318831
GiGs
Pro
Sheet Author
API Scripter
You want to change the default for new characetrs, but old characters keep the old default? If I understand, the way to do that is with a version script, and the logic would be very tricky. You'd need the following steps Check if its an already created character (this is the tricky part - maybe look for something that is always changed). If its an already created character, run a version change: change all attributes that are at default from to the new value (which means they have an actual value, not a default). If it's a new character, do nothing. That seems the most straightforward way I can think of, but that first step can be very tricky - it depends how the system works.
1659413475
vÍnce
Pro
Sheet Author
Old characters that have never changed the attribute from it's default of 10 should remain at 10, but all new characters would start with a default of 8. Thought; can't I leave the HTML value at 10 , test for a new sheet. (maybe sheet_version = final_version && test against some empty/null attributes) and only change new sheets to 8?
1659449269
GiGs
Pro
Sheet Author
API Scripter
You have two problems: First, you need some way to identify a new sheet vs an old one. If you use a version attribute, the traditional method is to create a hiddent attribute, that on first load, the wheet worker runs to make changes, and then updates the version attribute. The problem here is on first load, all characters will be seen as new characters. So you need to check for an attribute that lready exists and that every single character will have changed at some point. If there is an attribute (like, say, class) that everyone has to select before they can use the character, you can check that attribute. Or a repeating section that everyone uses, and check if it has any entries. If there isn't a single attribute, you might have to search every attribute, and check if any one have a value that is not equal to its default. If even one is not default, it's an old character. But for that, you'd have to build an array with a possibly huge list. Once you have that array, it's an easy check though. The second problem is not really a process, it's a process. You'll need a huge array of all attributes with a default value (it could be the same array in the previous problem, or a separate array of only those attributes that have a default of 10 that is being changed to 8), and once you know a sheet is an old character, you need to read the current value of each of those attributes, and if its equal to default (so it hasnt been changed by the user), set it to a value of 10. Then you can set the default value to all those attributes to 8. So, it's not straightforward. If you're only changing the default of a small number of stats, and you are using a class-based system (and the sheet doesn't have a default class), it would be fairly easy.
1659466261

Edited 1659476156
vÍnce
Pro
Sheet Author
I think I've come up with a method the seems to be working.  The sheet goes through some checks when it's opened.  I also added new_character flag attribute to help with future checks (thanks Richard). So basically on sheet: opened; 1. If the current version === final version it's either an older updated sheet(already has gone through any version updates) or it's a brand new sheet so, 2. Continue by testing for old vs new using new_character.  If it's 0, bail out, otherwise continue.  (note: as you mentioned, every sheet will be 1 to start with but this should help for an earlier bail out for future checks.) 3. Continue by testing a handful of specific attributes (ie hitdice, ac, and the 6 ability scores) to see if they meet the criteria of a new sheet.  If they do not ALL pass, bail out. 4. If the sheet passes all that, it will test each ability score. If the ability score is not "10", do not update the score, otherwise update to "8". Recalc the rows once they have been updated. No one is the wiser that the sheet went from default 10 ability scores to 8. Thanks for your help GiGs // Check and set Ability defaults to 8 on new sheets const newSheet = () => { getAttrs(['hitdice', 'armorclass', 'strength', 'intelligence', 'wisdom', 'dexterity', 'constitution', 'charisma', 'new_character'], (v) => { const output = {}; const testHitdice = +v.hitdice; const testAC = +v.armorclass; const testStr = +v.strength; const testInt = +v.intelligence; const testWis = +v.wisdom; const testDex = +v.dexterity; const testCon = +v.constitution; const testCha = +v.charisma; const testNewChar = +v.new_character; // new sheets will have all abilities '10' by default // defaults will then be set to '8' default const testAbility = testStr + testInt + testWis + testDex + testCon + testCha; clog(`~~~~~~ Average Ability detected: ${testAbility}, Hit Dice: ${testHitdice}, AC: ${testAC}`); if (testNewChar === 0) { clog(`~~~~~~ OLD SHEET DETECTED: ${testNewChar}`); } else if (testHitdice === 1 && testAC === 10 && testAbility === 60) { clog(`~~~~~~ NEW SHEET DETECTED: ${testNewChar}, Ability Defaults set to "8".`); output.strength = testStr === 10 ? 8 : testStr; output.intelligence = testInt === 10 ? 8 : testInt; output.wisdom = testWis === 10 ? 8 : testWis; output.dexterity = testDex === 10 ? 8 : testDex; output.constitution = testCon === 10 ? 8 : testCon; output.charisma = testCha === 10 ? 8 : testCha; output.new_character = 0; } else if (testNewChar >= 0) { clog(`~~~~~~ OLD SHEET DETECTED: ${testNewChar}`); output.new_character = 0; } setAttrs(output, { silent: true, }); strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }); };
1659499706

Edited 1659499724
GiGs
Pro
Sheet Author
API Scripter
Testing for armour class and hit dice, as well as the stats, makes a lot of sense. Hard to believe that a starting character will have the same values in all of those. This part of the function makes me worry: setAttrs(output, { silent: true, }); strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }); };          I don't know what those functions do, but because of synchronicity, there is no guarantee that they will run after the setAttrs updates the values. (It'll usually happen in testing, but an actual game play, with different players opening sheets at different times, and creating load on the system, it might not.) If that's important you should use the callback function of setAttrs, which guarantees somethng only runs after setAttrs has finished, like so:: setAttrs(output, { silent: true, }, stat_functions()); ); }; const stat_functions = () => { strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }; If those functions don't read values from the sheet, but use values you just created, you'd want to pass the output function to it. If they do each read and set values, it would be a good idea to combine them into a single function, or have them return values (and have a single setAttrs after the functions to save the returned values).
1659500439

Edited 1659502463
vÍnce
Pro
Sheet Author
The first time I called those functions (each one basically recalcs the ability row's sub-attributes) by calling another updateAbilities() function that runs them all, but doing so caused a cascading loop.  So, added silent true and called them each individually. But I get your point.  I've heard of callbacks. lol I think I can follow your example.  There's probably more than one area where I should create callbacks.
1659501942

Edited 1659502031
GiGs
Pro
Sheet Author
API Scripter
Callbacks are complicated, and I tried to avoid them except where necessary - but its worth mentioning that you are already using them. getAttrs for example does this: getAttrs([stats], function (scan sheet and save those stats into a variable, this is a function) {     this entire setion is a callback that runs after the getattrs function grabs stat values from the sheet }); Many Roll20 functions are built to explicitly include callbacks, and they can be just understand as "another function that runs after the main one". With setAttrs you have: setAttrs({function to save stats},     {option to set silent}, {option to call another function here which runs after setAttrs has completed - the callback} );
1659504928

Edited 1659505676
vÍnce
Pro
Sheet Author
Strange.  Adding the callback doesn't seem to work.  New sheets never get past clog(`~~~~~~ Average Ability detected: ${testAbility}, Hit Dice: ${testHitdice}, AC: ${testAC}`); the log shows that the sheet should be new, but it doesn't seem to ever step through the else if check now.  I've removed the last else if because I don't think its necessary since testNewChar is already tested for both states.  I also moved stat_functions() above newSheet() because I was getting warning from eslint about using a variable before it was defined.  stat_functions() works because it can be called from another sheet update function and it calcs without error. Never-mind. I had changed a test in the else if erroneously that was causing new sheets to fail the check. Callback works! ;-) Thanks GiGs
1659506687
GiGs
Pro
Sheet Author
API Scripter
Whew! One thing I'd change put the setAttrs inside the branch of the if where changes are being made, like          if ( testNewChar === 0 ) {             clog ( `~~~~~~ OLD SHEET DETECTED: ${ testNewChar } ` );           } else {             clog ( `~~~~~~ NEW SHEET DETECTED: ${ testNewChar } , Ability Defaults set to "8".` );             output . strength = testStr === 10 ? 8 : testStr ;             output . intelligence = testInt === 10 ? 8 : testInt ;             output . wisdom = testWis === 10 ? 8 : testWis ;             output . dexterity = testDex === 10 ? 8 : testDex ;             output . constitution = testCon === 10 ? 8 : testCon ;             output . charisma = testCha === 10 ? 8 : testCha ;             output . new_character = 0 ;             setAttrs (                 output ,                 {                 silent: true ,                 },                 stat_functions ()             );           }          As it is, the stat_functions function will run for all characters, and you probabky only want it to run where things have changed.
1659507275

Edited 1659507495
vÍnce
Pro
Sheet Author
Spoke too soon.  It looks like it doesn't always finish converting all the abilities to "8", or it's having sync issues with the final ternary checks for some reason? idk output.strength = testStr === 10 ? 8 : testStr; output.intelligence = testInt === 10 ? 8 : testInt; output.wisdom = testWis === 10 ? 8 : testWis; output.dexterity = testDex === 10 ? 8 : testDex; output.constitution = testCon === 10 ? 8 : testCon; output.charisma = testCha === 10 ? 8 : testCha; Opened 6 new characters and they all get the same stats... Why would it work on some but not others? current code; const stat_functions = () => { strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }; // Check and set Ability defaults to 8 on new sheets const newSheet = () => { getAttrs(['hitdice', 'armorclass', 'strength', 'intelligence', 'wisdom', 'dexterity', 'constitution', 'charisma', 'new_character'], (v) => { const output = {}; const testHitdice = +v.hitdice; const testAC = +v.armorclass; const testStr = +v.strength; const testInt = +v.intelligence; const testWis = +v.wisdom; const testDex = +v.dexterity; const testCon = +v.constitution; const testCha = +v.charisma; const testNewChar = +v.new_character; // new sheets will have all abilities '10' by default // defaults will then be set to '8' default const testAbility = testStr + testInt + testWis + testDex + testCon + testCha; clog(`~~~~~~ Average Ability detected: ${testAbility}, Hit Dice: ${testHitdice}, AC: ${testAC}`); if (testNewChar === 0) { clog(`~~~~~~ OLD SHEET DETECTED: ${testNewChar}`); } else if (testHitdice === 0 && testAC === 10 && testAbility === 60) { clog(`~~~~~~ NEW SHEET DETECTED: ${testNewChar}, Ability Defaults set to "8".`); output.strength = testStr === 10 ? 8 : testStr; output.intelligence = testInt === 10 ? 8 : testInt; output.wisdom = testWis === 10 ? 8 : testWis; output.dexterity = testDex === 10 ? 8 : testDex; output.constitution = testCon === 10 ? 8 : testCon; output.charisma = testCha === 10 ? 8 : testCha; output.new_character = 0; setAttrs( output, { silent: true, }, stat_functions() ); } }); };
1659507716

Edited 1659507775
GiGs
Pro
Sheet Author
API Scripter
My initial reaction is "i have no idea". I think it has to be this test: === 10 ? 8 : testInt; Sometimes it's triggering, others it isnt. Are you sure all of the stats are actually changing or being checked correctly? Put a log at the start of the else branch, with the current values of the variables to be changed, like else {       clog(`~~~~~~ NEW SHEET DETECTED: ${testNewChar}, Ability Defaults set to "8".`); console.info({testStr, testInt, testWis, testDex, testCon, testCha}); and see if their values are what they should be. If they are all getting the same results, there is some issue that is consistent.
1659507900

Edited 1659508236
GiGs
Pro
Sheet Author
API Scripter
Unrelated, I'd change the newCharacter stuff to oldCharacter, and set value to 1. Having things that you want to test for at value = 0 can sometimes cause issues in JS. You never want to be testing for a value of 0. So, like const testOldChar = +v.old_character; // skip if (testOldChar === 1) { //skip } else if (testHitdice === 0 && testAC === 10 && testAbility === 60) { //skip output.old_character = 1; and change the reference in getAttr[/* stats *, old_character]
1659508388
vÍnce
Pro
Sheet Author
new log posted all stats at 10.  I also just tried just setting them without a check         output . strength = 8 ;         output . intelligence = 8 ;         output . wisdom = 8 ;         output . dexterity = 8 ;         output . constitution = 8 ;         output . charisma = 8 ;       And it still does the same to the stats.
1659508641
GiGs
Pro
Sheet Author
API Scripter
It's the same stats not being changed every time? Is it a visual glitch. When you close the sheet and reoopen it, have they changed?
1659508875

Edited 1659508899
vÍnce
Pro
Sheet Author
Same stats.  Not a glitch.  I verified by grabbing the value @{selected|strength}, etc. I changed to oldCharacter and adjusted attribute and js as you suggested. current code const stat_functions = () => { strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }; // Check and set Ability defaults to 8 on new sheets const newSheet = () => { getAttrs(['hitdice', 'armorclass', 'strength', 'intelligence', 'wisdom', 'dexterity', 'constitution', 'charisma', 'new_character'], (v) => { const output = {}; const testHitdice = +v.hitdice; const testAC = +v.armorclass; const testStr = +v.strength; const testInt = +v.intelligence; const testWis = +v.wisdom; const testDex = +v.dexterity; const testCon = +v.constitution; const testCha = +v.charisma; const testOldChar = +v.new_character; // new sheets will have all abilities '10' by default // defaults will then be set to '8' default const testAbility = testStr + testInt + testWis + testDex + testCon + testCha; clog(`~~~~~~ Average Ability detected: ${testAbility}, Hit Dice: ${testHitdice}, AC: ${testAC}`); if (testOldChar === 1) { clog(`~~~~~~ OLD SHEET DETECTED: ${testOldChar}`); } else if (testHitdice === 0 && testAC === 10 && testAbility === 60) { clog(`~~~~~~ NEW SHEET DETECTED: ${testOldChar}, Ability Defaults set to "8".`); output.strength = testStr === 10 ? 8 : testStr; output.intelligence = testInt === 10 ? 8 : testInt; output.wisdom = testWis === 10 ? 8 : testWis; output.dexterity = testDex === 10 ? 8 : testDex; output.constitution = testCon === 10 ? 8 : testCon; output.charisma = testCha === 10 ? 8 : testCha; output.old_character = 1; setAttrs( output, { silent: true, }, stat_functions() ); } }); };
1659509007

Edited 1659509235
vÍnce
Pro
Sheet Author
Maybe I'll try w/out the callback function?  It just recalcs so the row's stats update according to the primary stat.
1659509220

Edited 1659509567
vÍnce
Pro
Sheet Author
seems to work again if I remove the callback. lol
1659509436

Edited 1659511372
vÍnce
Pro
Sheet Author
I just noticed that these individual function calls const stat_functions = () => { strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); }; also set the ability... example of charisma() charismaCalcs = () => { getAttrs(['charisma'], (values) => { const output = {}; const stat_cha = parseValues(values, 'charisma', 'int'); output.charisma = stat_cha; output.maximumhenchmen = AT_CHA.getEntry(stat_cha).MaximumHenchmen(); output.loyaltybonus = AT_CHA.getEntry(stat_cha).getLoyaltyBonus(); output.reactionbonus = AT_CHA.getEntry(stat_cha).getReactionBonus(); output.comeliness_cha_adj = AT_CHA.getEntry(stat_cha).getComeliness(); setAttrs(output, { silent: true, }); }); }; I suppose that is interfering with things?  But I thought the idea of the callback was to happen AFTER the setAttrs was done.
1659510446

Edited 1659510801
vÍnce
Pro
Sheet Author
If I keep the callback in newSheet() and I remove setting the ability to itself in the individual ability calcs I did this to force setting a default value when ability auto-calcs were added as sheet update, but it's probably not necessary now. ie charismaCalcs() output.charisma = stat_cha; It works.  But again, as per using a callback, shouldn't these functions not even run until newSheet() is done with setAttrs?
1659525428
GiGs
Pro
Sheet Author
API Scripter
Using the callback, those functions shouldn't run until newSheet function has completely finished. I use this technique extensively with version changing functions, and havemnt seen an issue before. I'll have to look more closely at your functions. Which if the 6 stats are not being corrected properly? Also in your stat functions, what is this: AT_CHA.getEntry(stat_cha)
1659537224

Edited 1659591702
vÍnce
Pro
Sheet Author
I'll have to test each of the ability calcs individually(little later today). But if I un-comment the line that writes the current value to the sheet, example; const stat_cha = parseValues(values, 'charisma', 'int'); output.charisma = stat_cha; (note I was doing this to all six functions), newSheet would seemingly only update INT and CHA. (see images above) If I leave the line out of all six, all six update. The AT_CHA.getEntry line is used to get data from a lookup table.  Each stat has a lookup table that matches the current stat value(3-25), to grab the matching array of values to be used for the ability's row of "sub-attributes".  Not my code.  It never is.  lol  But I have been able to at least make some heads and tales of the process.
1659592542

Edited 1659592752
vÍnce
Pro
Sheet Author
Did a little more testing with this.  Allowing any or all of the ability calcs strengthCalcs(); intelligenceCalcs(); wisdomCalcs(); dexterityCalcs(); constitutionCalcs(); charismaCalcs(); to set the primary attribute output.charisma = stat_cha; seems to break newSheet()'s ability to re-write the abilities from 10 to 8.  There isn't always a pattern for which ones stay at the default of 10. Meaning, I can comment out all of them except for wisdom, and wisdom is not the only ability that stays at 10. So I think enabling it even once in this flow, throws newSheet() out of sync. IDK for sure. ;-(   Truthfully, I'm really not sure I need the ability recalc functions to write to itself anyway.  I think I added that to somehow differentiate those attribute values from their defaults.  But given that they are called only to set the sub-attributes and are normally only triggered when you actually change the ability attribute, it seems like a useless step. My solution has been to just not set the primary attribute to itself.  newSheet() and normal ability recalcs seems to work fine.
1659603461
GiGs
Pro
Sheet Author
API Scripter
I don't think I'll be able to analyse the problem without a full copy of the sheet (html and css) to try to figure out what's going on.
1659625398
vÍnce
Pro
Sheet Author
As much as I would LOVE for you to analyze the entire shhet, I wouldn't do that to you GiGs.  Your efforts are best served to help the whole vs the one... Thank you
1659666036
GiGs
Pro
Sheet Author
API Scripter
lol, thank you :)