coincidentally, I have been planning a post on how to write sheetworkers that apply to multiple stats like this, so this is a bit of practice for it :)
Here's the basics of how I would rewrite the above script. For this first draft, there are some stats that dont work, I'll explain afterwards and mention how to fix it for those too.
First you create an array of the stats needed like this:
const traits = ['agility', 'smarts', 'spirit', 'strength', 'vigor', 'fighting', 'academicsskill', 'athletics',
'battle', 'boating', 'climbing', 'commonknowledge', 'driving', 'electronics', 'faith', 'focus', 'gambling',
'guts', 'hacking', 'healing', 'intimidation', 'investigation', 'language', 'lockpicking', 'notice', 'occult',
'performance', 'persuasion', 'piloting', 'psionics', 'repair', 'research', 'riding', 'ritual', 'science',
'shooting', 'spellcasting', 'stealth', 'streetwise', 'survival', 'swimming', 'taunt', 'throwing', 'thievery',
'tracking', 'weirdscience', 'repeating_skills:skillnamerank', ];
You'll notice a bunch of traits are missing from that list - all the ones with 'mod' in their name.
Then you follow that immediately with this sheet worker:
traits.forEach(trait => {
on(`change:${trait} change:${trait}mod`,function(eventInfo) {
getAttrs([trait,`${trait}Mod`], v => {
let tRank = '1d'+v[trait]+'!';
let tDesc = 'd' + v[trait];
let intMod = parseInt(v[`${trait}_mod`])||0;
if(intMod !== 0) {
tRank += `+${intMod}[trait]`;
tDesc += '+' + intMod;
}
const sAttRank = trait + '_rank', sAttDisplay = trait + '_display';
const sattrs = [];
sattrs[sAttRank]=tRank;
sattrs[sAttDisplay]=tDesc;
setAttrs(sattrs);
)};
});
)};
I think I've decoded what your script does, and if so, the above should do it. It's much more compact and dare i say, elegant, approach than the original code.
Thanks to the forEach loop, this creates a unique sheet worker for each trait and trait mod pair, so when the worker runs, you always know which trait is being called, and dont need eventInfo.
Before I get to the cases the above doesnt cover, let me first mention a bit of syntax:
`change:${trait} change:${trait}mod`
On this line I used a string literal. This line is exactly the same as writing
"change:" + trait + " change:" + trait + "mod"
Once you're used to it though, the first version (string literal aka template literal) is a lot easier to handle when you writing strings that contain multiple variables.
But use whichever you're most comfortable with.
This overall approach is easy to maintain: if you later add more attributes, you can just add their names to the array at the start and don't need to edit the code at all. The method is also easily transferrable to other sheet projects. It's a great approach to handling this kind of situation where you have one rule that applies to lots of different attributes.
So on to the attributes the above doesnt quite work with and why. First point: I notice some inconsistency in the "Mod" part of names - some are listed as Mod in the change line, and they should always be lower case there. I am assuming for this script that the Mod in attribute names i always capitalised.
The big issue: For most of your attributes you use a consistent "trait" and "traitmod" naming scheme. For the core stats (agility, smarts, etc) and the repeating set, you don't do that. Which means they aren't as simple to handle.
The two best ways to ways to address this, in order:
- since you're doing a version update script, take the opportunity to rename the affected attributes so they do match the pattern ([trait], [trait]+mod) and transfer all the affected existing attributes to the new ones. It's more work initially, but i encourage you to do it.
- Alternatively, add in a little special cases check at the start of the above script so that it does work for them.
Here's what the special cases version would look like:
traits.forEach(trait => {
const specialcases = ['agility', 'smarts', 'spirit', 'strength', 'vigor', 'repeating_skills:skillname', ]
let mod = trait + 'Mod', display = trait + "_display, rank = "_rank";
if(specialcases.includes(trait) ) {
if (trait.slice(0,16) === 'repeating_skills') {
mod = repeating_skills_SkillNameMod;
rank = 'repeating_skills_otherskill-rank'
display = 'repeating_skills_otherskill-display'
} else {
mod = trait.slice(0,2) + 'Mod';
}
}
on(`change:${trait.toLowerCase()} change:${mod.toLowerCase()}`,function() {
getAttrs([trait, mod], function(v) {
let tRank = '1d'+ v[trait]+'!';
let tDesc = 'd' + v[trait];
let intMod = parseInt(v[mod)]||0);
if(intMod !== 0) {
tRank += `+${intMod}[${trait}]`;
tDesc += '+' + intMod;
}
const sattrs = [];
sattrs[rank]=tRank;
sattrs[display]=tDesc;
setAttrs(sattrs);
)};
});
)};
The specialcases array and the following if statement create the mod stat you need.
I should stress I havent tested the above, so I cant rule out the possibilities of minor typos and syntax errors, but the principle is sound!
One question: does intMod ever go negative? If you you'd need to alter one section of the script (both mine and yours):
if(intMod !== 0) {
tRank += `+${intMod}[${trait}]`;
tDesc += '+' + intMod;
}
should be something like the following to avoid getting situations like "+-"
if(intMod > 0) {
tRank += `+${intMod}[${trait}]`;
tDesc += '+' + intMod;
} else if(intMod < 0) {
tRank += `${intMod}[${trait}]`;
tDesc += intMod;
}
or (if you are familiar with the ternary operator):
if(intMod !== 0) {
tRank += (intMod > 0 ? '+' : '') + `${intMod}[${trait}]`;
tDesc += (intMod > 0 ? '+' : '') + intMod;
}
I hope you find all this useful!