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

Generic Functions

June 17 (7 years ago)

Edited June 17 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
I found a thread talking about generic functions.  If I can figure this out, it would help me a lot.  Is there anyone who can explain this with colored markers and big poster boards? 

I am making a sheet for the Cortex system, and it uses a lot of "steps", starting at d0, d2, d4 and going all the way to d12+d2, d12+d4, etc.  I was hoping to get one set of sheet workers to use when something is increased a few steps at a time. 
June 17 (7 years ago)
Thought I'd mention that if you'd prefer to use a macro (benefit is being able to use Roll Queries to modify step number), I've written a few macros for Earthdawn's step system. Assuming it works the same way, I'd just need a picture of the step table to write a macro for it
June 17 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
I actually want to set this up using sheet workers, and do it that way.  Right now, I can make a series of a hundred sheet workers to do what I want, and I am hoping for a across the board fix.

Macros...macros have a tendency to drive me (more) bonkers.
June 17 (7 years ago)
Lithl
Pro
Sheet Author
API Scripter
The problem that the code is trying to solve is having several blocks of code that are similar, and creating a function that will do those repeated actions with the small differences replaced by parameters. Exactly what that entails varies widely by the logic you're trying to achieve.
June 17 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
Renaming things to make the code works is doable.  Right, so this is what I have, and works; I would love to make an easy set of code everything can use, instead of 47 sheet workers with only name changes: 
    //Step Calc part 1
on("change:Agility_select change:Agility_mod sheet:opened", function() {
        getAttrs(["Agility_select", "Agility_mod"], function(values) {
            setAttrs({
                Agility_subtotal: +values.Agility_select + +values.Agility_mod
            });
        });
    });
    //Step Calc part 2
on("change:Agility_subtotal sheet:opened", function() {
    getAttrs(["Agility_subtotal"], function(values) {

        if (values.Agility_subtotal == 0) {
            setAttrs({ 
                Agility: 'd0'
            })
        }
		else if (values.Agility_subtotal == 1) {
            setAttrs({ 
				Agility: 'd2'
            })
        }
		else if (values.Agility_subtotal == 2) {
            setAttrs({ 
				Agility: 'd4'
            })
        }
		else if (values.Agility_subtotal == 3) {
            setAttrs({ 
				Agility: 'd6'
            })
        }
		else if (values.Agility_subtotal == 4) {
            setAttrs({ 
				Agility: 'd8'
            })
        }
		else if (values.Agility_subtotal == 5) {
            setAttrs({ 
				Agility: 'd10'
            })
        }
		else if (values.Agility_subtotal == 6) {
            setAttrs({ 
				Agility: 'd12'
            })
        }
		else if (values.Agility_subtotal == 7) {
            setAttrs({ 
				Agility: 'd12+d2'
            })
        }
		else if (values.Agility_subtotal == 8) {
            setAttrs({ 
				Agility: 'd12+d4'
            })
        }
		else if (values.Agility_subtotal == 9) {
            setAttrs({ 
				Agility: 'd12+d6'
            })
        }
		else if (values.Agility_subtotal == 10) {
            setAttrs({ 
				Agility: 'd12+d8'
            })
        }
		else if (values.Agility_subtotal == 11) {
            setAttrs({ 
				Agility: 'd12+d10'
            })
        }
		else if (values.Agility_subtotal == 12) {
            setAttrs({ 
				Agility: 'd12+d12'
            })
        }
    });
});
June 17 (7 years ago)

Edited June 17 (7 years ago)
Jakob
Sheet Author
API Scripter
Like this? (Remark: I think you need to lowercase your attribute names in all events).

var stats = ['Agility', 'Wits', 'Strength'];

stats.forEach(stat => {
  'use strict';
  on(`change:${stat.toLowerCase()}_select change:${stat.toLowerCase()}_mod sheet:opened`, () => {
    getAttrs([`${stat}_select`, `${stat}_mod`], function(values) {
      let setting = {};
      setting[`${stat}_subtotal`] = +values[`${stat}_select`] + +values[`${stat}_mod`];
      setAttrs(setting);
    });
  });

  on(`change:${stat.toLowerCase()}_subtotal sheet:opened`, function() {
    let dieFromSubtotal = {
      '0': 'd0',
      '1': 'd2',
      '2': 'd4',
      '3': 'd6',
      '4': 'd8',
      '5': 'd10',
      '6': 'd12',
      '7': 'd12+d2',
      '8': 'd12+d4',
      '9': 'd12+d4',
      '10': 'd12+d8',
      '11': 'd12+d10',
      '12': 'd12+d12'
    };
    getAttrs([`${stat}_subtotal`], function(values) {
      let setting = {};
      setting[stat] = dieFromSubtotal[values[`${stat}_subtotal`]]; // Might want to add some defaults here in case something is undefined
      setAttrs(setting);
    });
  });
});
I don't if there's a whole lot to explain, basically, you need to replace every string with Agility in it by the templated version, which also requires you to write the objects a bit differently, because you cannot use a variable name as the key for an object if you write it as a literal (or at least I don't know how you could do it).
June 18 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
I will try it out.  Thank you
June 19 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
This works great.  Thank you very much.

Another question: does this work at all with repeating fields?
June 19 (7 years ago)
Jakob
Sheet Author
API Scripter

Coal Powered Puppet said:

This works great.  Thank you very much.

Another question: does this work at all with repeating fields?

Yes, with the right alterations depending on what exactly you want to do :). If you have a bunch of repeating sections, with a bunch of attributes within each section, plus a bunch of non-repeating attributes whose changes you still care about to calculate stuff within the section, you will probably have to loop over several objects to do it.
June 19 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
So far, all I have done is use this code for the attributes and non-repeating skills.  This is amazing, Jakob.  Thank you.
July 08 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
Oh, Master Jacob, Wandering master of all things Sheet Workers...I got another issue you help me with.

The idea is that when the player selects something from a drop down (foo_attrib) it will populate three fields (foo_AttName, foo_AttRating, and foo_EpicRating).  foo_AttName is suppose to be the name of an attribute, foo_AttRating is a number equal to the corresponding @{attribute}, and foo_EpicRating is equal to foo_box.  

Here is what I got, and it is not working.  What did I do wrong?

var stats = ['Appearance','Charisma','Dexterity','Intelligence','Manipulation','Perception', 'Strength', 'Stamina', 'Wits', 'Appearance_box','Charisma_box','Dexterity_box','Intelligence_box','Manipulation_box','Perception_box', 'Strength_box', 'Wits_box', 'Stamina_box',];

stats.forEach(stat => {
  'use strict';
  on(`change:${stat.toLowerCase()}_attrib change:${stat.toLowerCase()}_box sheet:opened`, () => {
    getAttrs([`${stat}_attrib`, `${stat}_box`], function(values) {
      let setting = {};
      setting[`${stat}_AttRating`] = ${stat};
      setAttrs(setting);
    });
  });


  on(`change:${stat.toLowerCase()}_AttName sheet:opened`, function() {
    let dieFromSubtotal = {
      '0': 'None',
      '1': 'Appearance',
      '2': 'Charisma',
      '3': 'Dexterity',
      '4': 'Intelligence',
      '5': 'Manipulation',
      '6': 'Perception',
      '7': 'Stamina',
      '8': 'Strength',
      '9': 'Wits'
    };
    getAttrs([`${stat}_box`], function(values) {
      let setting = {};
      setting[`${stat}_EpicRating`] = [values[`${stat}_box`];
      setAttrs(setting);
    });
  });
});
July 08 (7 years ago)
Jakob
Sheet Author
API Scripter
So, the code is all kinds of wrong, but I don't even completely understand what you want to do, and in some cases it conflicts with what you have actually written in the code.

So, you have one select, or multiples (one for each attribute)? What is the name of the select/selects (as in name="attr_???")? What are the option values for the select(s)?
Once we have that, maybe you can explain how exactly the value of the ${foo}_AttName, ${foo}_AttRating and ${foo}_EpicRating attributes are determined from the select/selects.

Because from what I've reading here, you have one select for each attribute, and can select any of the attributes in each of them, and that seems to make no sense.
July 08 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author

Jakob said:

So, the code is all kinds of wrong...
S it is worse than I thought.  Okay.  Here is what I want it to do, for a single drop-down:

on("change:Academics_attrib  change:Strength_box change:Dexterity_box change:Stamina_box change:Charisma_box change:Manipulation_box change:Appearance_box change:Intelligence_box change:Wits_box change:Perception_box change:Strength change:Dexterity change:Stamina change:Charisma change:Manipulation change:Appearance change:Intelligence change:Wits change:Perception sheet:opened", function() {
    getAttrs(['Academics_attrib', 'Perception', 'Wits', 'Intelligence', 'Appearance', 'Manipulation', 'Charisma', 'Stamina', 'Dexterity', 'Strength', 'Perception_box', 'Wits_box', 'Intelligence_box', 'Appearance_box', 'Manipulation_box', 'Charisma_box', 'Stamina_box', 'Dexterity_box', 'Strength_box'], function(values) {
            if (values.Academics_attrib == 1 ) {
                setAttrs({
                    Academics_AttName: 'Appearance',
                    Academics_AttRating: values.Appearance,
                    Academics_EpicRating: values.Appearance_box
                })
            }
            else if (values.Academics_attrib == 2 ) {
                setAttrs({
                    Academics_AttName: 'Charisma',
                    Academics_AttRating: values.Charisma,
                    Academics_EpicRating: values.Charisma_box
                })
            }
            else if (values.Academics_attrib == 3 ) {
                setAttrs({
                    Academics_AttName: 'Dexterity',
                    Academics_AttRating: values.Dexterity,
                    Academics_EpicRating: values.Dexterity_box
                })
            }
            else if (values.Academics_attrib == 4 ) {
                setAttrs({
                    Academics_AttName: 'Intelligence',
                    Academics_AttRating: values.Intelligence,
                    Academics_EpicRating: values.Intelligence_box
                })
            }
            else if (values.Academics_attrib == 5 ) {
                setAttrs({
                    Academics_AttName: 'Manipulation',
                    Academics_AttRating: values.Manipulation,
                    Academics_EpicRating: values.Manipulation_box
                })
            }
            else if (values.Academics_attrib == 6 ) {
                setAttrs({
                    Academics_AttName: 'Perception',
                    Academics_AttRating: values.Perception,
                    Academics_EpicRating: values.Perception_box
                })
            }
            else if (values.Academics_attrib == 7 ) {
                setAttrs({
                    Academics_AttName: 'Stamina',
                    Academics_AttRating: values.Stamina,
                    Academics_EpicRating: values.Stamina_box
                })
            }
            else if (values.Academics_attrib == 8 ) {
                setAttrs({
                    Academics_AttName: 'Strength',
                    Academics_AttRating: values.Strength,
                    Academics_EpicRating: values.Strength_box
                })
            }
            else if (values.Academics_attrib == 9 ) {
                setAttrs({
                    Academics_AttName: 'Wits',
                    Academics_AttRating: values.Wits,
                    Academics_EpicRating: values.Wits_box
                })
            }
            else if (values.Academics_attrib == 0 ) {
                setAttrs({
                    Academics_AttName: 'None',
                    Academics_AttRating: 0,
                    Academics_EpicRating: 'None'
                })
            }
        });
    }); 
Let me see if I can explain this a little better.  I am going to break this very far down to make sure I'm saying it right.  You, by all evidence, understand a much higher level of coding than I knew existed.
  • Player selects from a drop down menu (select) the last attribute (Wits, which is the option with a value "9").  
  • Code installs the name Wits in the name="attr_Academics_AttName" input.
  • Code installs the value of @{Wits} in the name="attr_Academics_AttRating" input.  This is a number
  • Code installs the value of @{Wits_box} in the name="attr_Academics_EpicRating" input.  This is also a number, pulled from a series of radio inputs.
  • Upon all of that, when the roll button is pressed, a roll template displays that Academics is added to Wits (rating whatever) and has an Epic Rating of (whatever).   
July 08 (7 years ago)

Edited July 08 (7 years ago)
Jakob
Sheet Author
API Scripter
Ah, thank you! Sometimes it's VERY helpful to know what exactly is going on. Part of what programming is about is to break down problems to their essence until they end up trivial :). Part of what had me confused is that there are two lists of attributes going on, stats (Charisma) on the one hand and skils (Academics) on the other hand.
So, the following does the same thing as what you are doing in the code above, except that the fixed 'Academics' is replaced by a loop over the skills array. I mention it in the code, but it would be simpler if the value of the dropdown was simply "Wits" and not "9", but if there is a good reason to keep the 9, then by all means, keep it.
var stats = ['Appearance', 'Charisma', 'Dexterity', 'Intelligence', 'Manipulation', 'Perception', 'Strength', 'Stamina', 'Wits'],
  skills = ['Academics', 'Underwater_Basketweaving'];
skills.forEach(function (skill) {
  'use strict';
  // relevantAttrs is e.g. ['Apperance', 'Charisma', ... , 'Appearance_box', ... , 'Academics_attrib']
  let relevantAttrs = [...stats, ...stats.map(stat => `${stat}_box`), `${skill}_attrib`];
  on(relevantAttrs.map(x => `change:${x.toLowerCase()}`) + ' sheet:opened', function () {
    getAttrs(relevantAttrs, function (values) {
      // Note: you could get rid of this step if the skill_attrib select just stored the name
      // of the attribute, and not some cryptic number.
      let numToStat = {
          '0': 'None',
          '1': 'Appearance',
          '2': 'Charisma',
          '3': 'Dexterity',
          '4': 'Intelligence',
          '5': 'Manipulation',
          '6': 'Perception',
          '7': 'Stamina',
          '8': 'Strength',
          '9': 'Wits'
        },
        attributeName = numToStat[values[`${skill}_attrib`]] || 'None',  // translate number to a stat name
        setting = {};
      // Set values according to known attributeName
      setting[`${skill}_AttName`] = attributeName;
      setting[`${skill}_AttRating`] = values[attributeName] || 0; // The "|| 0" is for the case of None.
      setting[`${skill}_EpicRating`] = values[`${attributeName}_box`] || 0;
      // Remark: EpicRating treats the case of None differently than your code, but I think 0 makes more sense here than None.
      setAttrs(setting);
    });
  });
});
July 08 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
So everything works when I reload the page.  How do I get it to work when the @{skill_attrib} is changed?

And what part- exactly- do I delete if I do as you suggested and change 0-9 to the actual names?  The story behind that is rather complicated.  Best describe as a series of scripting misadventures and confusion and not enough booze, which resulted in me going to the very, very basics.  Hence, numbers.  
July 08 (7 years ago)
Jakob
Sheet Author
API Scripter
Oops, I messed up the event. Replace the corresponding line by this (changes in bold)
  on(relevantAttrs.map(x => `change:${x.toLowerCase()}`).join(' ') + ' sheet:opened', function () {
Currently, the event string looks like ["change:appearance", ... "change:charisma", ... "change:academics_attrib"] sheet:opened, which of course does not work at all.

With regard to the change, suppose the select looks like this instead:
<select name="attr_academics_attrib">
 <option value="None">None</option>
<option value="Appearance">Appearance</option>
....
</select>
Then
1) you obviously don't need ${skill}_AttName anymore at all
2) you don't need the numToStat and attributeName variables in the above script
3) instead, the attributeName is just values[`${skill}_attrib`].

So the innermost function is something like this
getAttrs(relevantAttrs, function (values) {
  let setting = {};
  setting[`${skill}_AttRating`] = values[values[`${skill}_attrib`]] || 0; // The "|| 0" is for the case of None.
  setting[`${skill}_EpicRating`] = values[values[`${skill}_attrib`] + '_box'] || 0;
  setAttrs(setting);
});
July 08 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
This works, very well.  Amazingly well.  You, good sir, are a valuable resource. Thank you once again.
July 09 (7 years ago)
Jakob
Sheet Author
API Scripter
Happy to help.
July 11 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
New problem, one, and only one section of this code is not updating properly:
    //REAPTING mod value setting
var stats = ['Skill1','Skill2', 'Skill3', 'Skill4', 'Skill5', 'Skill6', 'Skill7', 'Skill8', 'Skill9', 'Skill10', 'Skill11', 'Skill12', 'Skill13', 'Skill14', 'Skill15', 'Skill19', 'Skill17', 'Skill18', 'Skill19', 'Skill20', 'Skill21', 'Skill22'];


stats.forEach(stat => {
  'use strict';
  on(`change:repeating_${stat}:${stat.toLowerCase()}_Spec_select change:repeating_${stat}:${stat.toLowerCase()}_Spec_mod sheet:opened`, () => {
    getAttrs([`repeating_${stat}_${stat}_Spec_select`, `repeating_${stat}_${stat}_Spec_mod`], function(values) {
      let setting = {};
      setting[`repeating_${stat}_${stat}_Spec_subtotal`] = +values[`repeating_${stat}_${stat}_Spec_select`] + +values[`repeating_${stat}_${stat}_Spec_mod`];
      setAttrs(setting);
    });
  });


  on(`change:repeating_${stat}:${stat.toLowerCase()}_Spec_subtotal sheet:opened`, function() {
    let dieFromSubtotal = {
      '0': 'd0',
      '1': 'd2',
      '2': 'd4',
      '3': 'd6',
      '4': 'd8',
      '5': 'd10',
      '6': 'd12',
      '7': 'd12+d2',
      '8': 'd12+d4',
      '9': 'd12+d4',
      '10': 'd12+d8',
      '11': 'd12+d10',
      '12': 'd12+d12',
      '13': 'd12+d12+d2',
      '14': 'd12+d12+d4',
      '15': 'd12+d12+d6',
      '16': 'd12+d12+d8',
      '17': 'd12+d12+d10',
      '18': 'd12+d12+d12'
    };
    getAttrs([`repeating_${stat}_${stat}_Spec_subtotal`], function(values) {
      let setting = {};
      setting[stat] = dieFromSubtotal[values[`repeating_${stat}_${stat}_Spec_subtotal`]]; // Might want to add some defaults here in case something is undefined
      setAttrs(setting);
    });
  });
});
I think, at the very end, "setting[stat]" is suppose to refer to "repeating_${stat}_${stat}" but I don't know how to name it right.  Am I making sense?
July 12 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
Never mind, I figurd it out.  I is SMRT!
July 18 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
Ooooooookay.  Hopefully the last time on this subject:

Right now, I am using this code (plus what ya fixed for me).  Within his code sum that is compared to the following, and spits out "d#"
on(`change:${stat.toLowerCase()}_subtotal sheet:opened`, function() {
    let dieFromSubtotal = {
      '0': 'd0',
      '1': 'd2',
      '2': 'd4',
      '3': 'd6',
      '4': 'd8',
      '5': 'd10',
      '6': 'd12',
      '7': 'd12+d2',
      '8': 'd12+d4',
      '9': 'd12+d4',
      '10': 'd12+d8',
      '11': 'd12+d10',
      '12': 'd12+d12'
    };
Now, I would like a code that does the same thing, but with adding something first.

I thought this would work, but it does not.
var stats = ['Skill1'];


stats.forEach(stat => {
  'use strict';
  on(`change:${stat.toLowerCase()}_select sheet:opened`, function() {
    let dieFromSubskill = {
      '0': 'd0',
      '1': 'd2',
      '2': 'd4',
      '3': 'd6',
      '4': 'd8',
      '5': 'd10',
      '6': 'd12',
      '7': 'd12+d2',
      '8': 'd12+d4',
      '9': 'd12+d4',
      '10': 'd12+d8',
      '11': 'd12+d10',
      '12': 'd12+d12'
    };
    getAttrs([`${stat}_select`], function(values) {
      let setting = {};
      setting[stat] = dieFromSubskill[values[`${stat}_ratiing`]]; // Might want to add some defaults here in case something is undefined
      setAttrs(setting);
    });
  });
});
July 19 (7 years ago)
Jakob
Sheet Author
API Scripter
You are trying to access values[`${stat}_ratiing`] (typo btw), but in the first argument for getAttrs, you are getting [`${stat}_select`]?
July 19 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
I want the sheet to get the values[`${stat}_select`],compare it to the list to find a die, and set values[`${stat}_rating`] to that die.

Stupid typo.  I will check to see if this works it.
July 19 (7 years ago)
Coal Powered Puppet
Pro
Sheet Author
Holy crap, your advice fix it.  I think the code knew I was talking to you and got intimidated.  Thank you!

This has caused me days of fustration.  Thank you again.
July 19 (7 years ago)
Jakob
Sheet Author
API Scripter

Coal Powered Puppet said:

Holy crap, your advice fix it.  I think the code knew I was talking to you and got intimidated.  Thank you!

This has caused me days of fustration.  Thank you again.

Ha!