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

Changing Attributes when Updating Sheets

1588444008

Edited 1588444631
MAMS Gaming
Pro
Sheet Author
Is there a guide or examples on how to preserve player data when altering attributes in a character sheet? If someone needs to see what I am working on, to give me an example, I am changing < option value =" 1d4 " > d4 </ option > (as well as d6,d8,d10,&d12) to <option value="d4">d4</option> I also want to move  < fieldset class =" repeating_principles " >     < input type =" text " name =" attr_principlename1 " >     < textarea name =" attr_principledesc1 " > </ textarea > </ fieldset > to only 2 iterations: <input type="text" name="attr_principle_name1"> <input type="text" name="attr_principle_name2"> <textarea name="attr_principle_rp1"></textarea> <textarea name="attr_principle_rp2"></textarea> I'm imagining a sheet worker that activates on "sheet:opened", but I figured I would ask people who have done it, before trying to write it from scratch.
1588446077

Edited 1588446147
GiGs
Pro
Sheet Author
API Scripter
There are several sheets in the repository that does this, using a pricess we call versioning. There are multiple ways to do but the principle is: Create a new attribute in the character sheet, with a default value of 0.  < input type =" text " name =" attr_version " value="0" > Create a sheet:opened  event, and check the verison attribute. If it is below value 1, run your version update. In your version update, make the changes you want, and also set version to 1 at the same time.  So, the first time the sheet runs after the new code is added, it will check the version value. It will be 0, so it runs the update.  The second and later times the sheet runs, it will check the version attribute, see that it is 1, and will not run the update. That way your update only runs once. This is important since there's no way to actually delete attributes in character sheets, so it would keep running and overwriting any new changes with the old data. You can extend this process with further updates, checking of the version is below 2, then 3, etc.       
1588447119
MAMS Gaming
Pro
Sheet Author
The third one is less necessary, and it seems more complicated, so i was going to leave it out. When I thought about how I would do it in Lua (the language I have been using for years), it was actually pretty easy, so I figure I should at least ask the JavaScript experts. The old version has 6 checkboxes: < input type =" checkbox " name =" attr_atk-action " > < input type =" checkbox " name =" attr_dfn-action " > < input type =" checkbox " name =" attr_ovc-action " > < input type =" checkbox " name =" attr_bst-action " > < input type =" checkbox " name =" attr_hin-action " > < input type =" checkbox " name =" attr_rec-action " > but the game only ever has 3 of them checked, so I replaced them with 3 buttons to hidden inputs that cycle which image is showing: <input type="hidden" class="icon-cycle" name="attr_icon1" value=0 /> <input type="hidden" class="icon-cycle" name="attr_icon2" value=0 /> <input type="hidden" class="icon-cycle" name="attr_icon3" value=0 /> The way to translate this would be to change attr_icon1 to a number based on the first of the checked boxes, and so on. Like I said, this is way beyond my understanding of html & JavaScript, but if you can help me make it happen, it would make players transferring their sheets a little happier.
1588465679
MAMS Gaming
Pro
Sheet Author
Well, I've been banging my head against this wall for a while now. I think I figured out where the problem is: I don't know how to access the value of the variables listed in my array. //Scripts for updating old character sheets on("sheet:opened", function() { getAttrs(["version","greendtype","yellowdtype","reddtype"], function(v) { let update = v.version; if (update < 1) { getSectionIDs("repeating_principles", principlesarray => { getSectionIDs("repeating_powers", powersarray => { getSectionIDs("repeating_qualities", qualitiesarray => { const output = {}; const diesizes = []; powersarray.forEach(id => diesizes.push(`repeating_powers_${id}_powerdtype`)); qualitiesarray.forEach(id => diesizes.push(`repeating_qualities_${id}_qualitydtype`)); diesizes.push("greendtype"); diesizes.push("yellowdtype"); diesizes.push("reddtype"); diesizes.forEach(id => { let str = v.id;//////////////////////////////////////////////////////////////////This Line //let str = v.greendtype; let res = str.substr(0,1); if (res == "1") { output[id] = str.substr(1); //output['greendtype'] = str.substr(1); }; }); setAttrs(output); }); }); }); }; }); }); If I replace that line with  let str = "1d10";    The rest of the code works, but I lose the previous data.
1588468997

Edited 1588469061
GiGs
Pro
Sheet Author
API Scripter
To answer your earlier question, this is how I structure my version upgrader.     const versionator = version => {         console.log("Version Checking.")         switch (true) {             case !version:             case version < 1:                 update_to_v1();                 break;             case version < 2:                 // add another function here when you need it                 break;             case version < 3:                 // ditto                 break;             default:                 console.log(`Version Update complete. Version: ${version}.`);         }     };     on('sheet:opened', () => {         getAttrs(['version'], v => {             versionator(parseFloat(v.version) || 0);         });     }); You have sheet:opened  event, which gets the sheet's version, then calls the versionator function. The versionator then checks if the sheet needs to be updated. If it does it calls a function to do the actual updating. This takes us to your last post. To be able to get sheet attributes, you need getAttrs - that's the only way to read attribute values off the sheet. And that takes us back to using fieldnames :) You also have getAttrs outside the getSectionIDs - thats never an efficient way to do it as it means you'll have to use two getAttrs. I;m not entirely sure what's going on in the body of your function so I havent attempted to do that, but here's a framework for how it should be laid out. function update_to_v1() {     getSectionIDs("repeating_principles", principlesarray => {     getSectionIDs("repeating_powers", powersarray => {     getSectionIDs("repeating_qualities", qualitiesarray => {         const fieldnames = [];         principlesarray.forEach(id => {                 // empty because i dont know what goes here         });         powersarray.forEach(id => {             fieldnames.push(`repeating_powers_${id}_powerdtype`);         });         qualitiesarray.forEach(id => {             fieldnames.push(`repeating_qualities_${id}_qualitydtype`);         });         getAttrs(["version","greendtype","yellowdtype","reddtype", ...fieldnames], function(v) {             let output = {};             //output.version = 1; //only uncomment this when your worker is fully tested and working.             principlesarray.forEach(id => {                         // do whatever you need to do here.             });             powersarray.forEach(id => {                          });             qualitiesarray.forEach(id => {                          });             setAttrs(output,                     {silent: true},                      versionator(update.version)));         });     });     });     }); }; notice the setAttrs line. {silent:true} means when setAttrs changes attributes, no sheet workers are triggered. You might want to set that to false if you depend on sheet values changing, but in an update function its best to avoid that because of the asynchronous nature of setAttrs and the fact multiple updates might be triggered simultaneously. There's no guarantee the sheet will update in the correct order, And finally setAttrs has a callback function. This calls the versionator again. This is important if you have a sheet that;s gone through, say, versions 1, 2, and 3, and someone opens their old version 0 sheet after a long time. You need all version updates to be carried out - this callback sends the worker back to the version checking function. At this point the sheet will have been updated to version 1, and if there's a version 2 update, it will now be done as well. The fact it is in the callback of setAttrs means the next version update can only happen after the first update has completed, avoiding the problems with async functions.
1588523643

Edited 1588524031
MAMS Gaming
Pro
Sheet Author
I tried implementing your code. I had to do some experimentation to figure out which part of it was breaking the code. There was an extra ) after the output. Even without it the output wouldn't trigger, and I had to remove the  versionator(update.version)  to get the rest of the output to work. Not sure why. Once I got the output working, I tried implementing my code again, and came across the same wall. We have the getAttrs retrieving the attributes, but I still can't figure out how to refer to them. getAttrs(["version","greendtype","yellowdtype","reddtype", ...fieldnames], function(v) { let output = {}; //output.version = 1; //only uncomment this when your worker is fully tested and working. let str = v.greendtype; let res = str.substr(0,1); if (res == "1") { output['greendtype'] = str.substr(1); }; str = v.yellowdtype; res = str.substr(0,1); if (res == "1") { output['yellowdtype'] = str.substr(1); }; str = v.reddtype; res = str.substr(0,1); if (res == "1") { output['reddtype'] = str.substr(1); }; principlesarray.forEach(id => { // do whatever you need to do here. }); powersarray.forEach(id => { str = `repeating_powers_${id}_powerdtype`;///////////////////////////////////////This Line res = str.substr(0,1); if (res == "1") { output[`repeating_powers_${id}_powerdtype`] = str.substr(1); }; }); qualitiesarray.forEach(id => { }); setAttrs(output, {silent: true}); }); I put in other output definitions so you can see what I'm trying to do. I test each string, and if the first digit is a '1', I remove it. (Turning '1d4' into 'd4', etc.)
1588528571
GiGs
Pro
Sheet Author
API Scripter
I'm not sure what's going on here             str = `repeating_powers_${id}_powerdtype`;///////////////////////////////////////This Line res = str.substr(0,1); if (res == "1") { output[`repeating_powers_${id}_powerdtype`] = str.substr(1); }; Is your goal just to copy the value of the repeating_powers:powerdtype attribute? If so what is its new attribute called?
1588529874
MAMS Gaming
Pro
Sheet Author
No new attribute. I'm changing the value of the Attribute. In the old version of the sheet, the value was "1dx" , but I just want "dx". My code tests the string's first digit, and if it's a "1", it removes it. I did it successfully on the non-repeating attributes shown above.
1588530704
GiGs
Pro
Sheet Author
API Scripter
Okay, you need to access the value of each attribute, which is done like this powersarray.forEach(id => { attribute_name = `repeating_powers_${id}_powerdtype`;///////////////////////////////////////This Line                     attribute_value = v[attribute_name]; res = attribute_value.substr(0,1); if (res == "1") { output[`repeating_powers_${id}_powerdtype`] = str.substr(1); }; }); I've used wordier variable names than I would normally so you can see more clearly what's going on
1588532013
MAMS Gaming
Pro
Sheet Author
Woot! Now that I understand that, I was able to abbreviate the code. I also figured out that "update" wasn't defined, so I tried replacing your  versionator(update.version)));  with  versionator(v.version));  and it seems to work now.     const fieldnames = []; principlesarray.forEach(id => { // empty because i dont know what goes here }); powersarray.forEach(id => { fieldnames.push(`repeating_powers_${id}_powerdtype`); }); qualitiesarray.forEach(id => { fieldnames.push(`repeating_qualities_${id}_qualitydtype`); }); fieldnames.push("greendtype"); fieldnames.push("yellowdtype"); fieldnames.push("reddtype"); getAttrs(["version", ...fieldnames], function(v) { let output = {}; //output.version = 1; //only uncomment this when your worker is fully tested and working. principlesarray.forEach(id => { // do whatever you need to do here. }); fieldnames.forEach(id => { let str = v[id]; let res = str.substr(0,1); if (res == "1") { output[id] = str.substr(1); }; }); setAttrs(output, {silent: true}, versionator(v.version)); }); Now I can try to work on the  principlesarray.forEach(id => {  which will require me to move the first 2 values of a repeating field, to 2 non-repeating fields. Wish me luck.
1588532638
MAMS Gaming
Pro
Sheet Author
I was wrong about  versionator(v.version));  fixing things. The rest of it still works, but I can't get the callback function to work 
1588534859

Edited 1588534884
GiGs
Pro
Sheet Author
API Scripter
v.version will be getting a string (attributes are stored as strings). Also, for testing purpioses, version isnt set to 1 yet - notice the line that is commented out. I forgot about that when posting the original code. For testing purposes, you should use setAttrs(output, {silent: true}, versionator(1)); That will stop the loop happening, but it will rerun every time you open the sheet. Which is what you want while testing. When testing is finished and you know the function works, change this to setAttrs(output, {silent: true}, versionator(output.version)); and just under the getAttrs line getAttrs([...fieldnames], function(v) { let output = {}; output.version = 1; Note you dont need version in the getAttrs line, since it will always be less than 1 if this worker runs, and you are setting it to 1.
1588535860
MAMS Gaming
Pro
Sheet Author
Thanks! I really do appreciate all your help.