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

July 31 (2 years ago)

Edited July 31 (2 years ago)
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

July 31 (2 years ago)
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

August 01 (2 years ago)
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.
August 02 (2 years ago)
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?

August 02 (2 years ago)
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.

August 02 (2 years ago)

Edited August 02 (2 years ago)
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();
});
};
August 03 (2 years ago)

Edited August 03 (2 years ago)
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).

August 03 (2 years ago)

Edited August 03 (2 years ago)
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.

August 03 (2 years ago)

Edited August 03 (2 years ago)
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}
);


August 03 (2 years ago)

Edited August 03 (2 years ago)
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

August 03 (2 years ago)
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.

August 03 (2 years ago)

Edited August 03 (2 years ago)
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()
);
}
});
};
August 03 (2 years ago)

Edited August 03 (2 years ago)
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.

August 03 (2 years ago)

Edited August 03 (2 years ago)
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]

August 03 (2 years ago)
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.

August 03 (2 years ago)
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?

August 03 (2 years ago)

Edited August 03 (2 years ago)
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()
);
}
});
};
August 03 (2 years ago)

Edited August 03 (2 years ago)
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.
August 03 (2 years ago)

Edited August 03 (2 years ago)
vÍnce
Pro
Sheet Author

seems to work again if I remove the callback. lol

August 03 (2 years ago)

Edited August 03 (2 years ago)
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.

August 03 (2 years ago)

Edited August 03 (2 years ago)
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?

August 03 (2 years ago)
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)


August 03 (2 years ago)

Edited August 04 (2 years ago)
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.

August 04 (2 years ago)

Edited August 04 (2 years ago)
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.

August 04 (2 years ago)
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.

August 04 (2 years ago)
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

August 05 (2 years ago)
GiGs
Pro
Sheet Author
API Scripter

lol, thank you :)