Character Vault
Any Concept / Any System
Compendium
Your System Come To Life
Roll20 for Android
Streamlined for your Tablet
Roll20 for iPad
Streamlined for your Tablet

Personal tools

Difference between revisions of "Sheet Worker Snippets"

From Roll20 Wiki

Jump to: navigation, search
m (See Also)
m (Function: parseValues)
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
''Main Article:'' '''[[Sheet Worker Scripts]]''' {{NavSheetDoc}}
+
{{revdate}}{{BCS}}''Main Article:'' '''[[Sheet Worker Scripts]]'''  
 +
 
 +
 
 +
Short code snippets to be used. See Also: [[Sheetworker_examples_for_Non-programmers|Sheetworker Examples for Non-programmers]]
 
<br>
 
<br>
 +
<br>
 +
{{NavSheetDoc}}
 +
__FORCETOC__
 
=Examples=
 
=Examples=
 
== Auto-calculating Attributes==
 
== Auto-calculating Attributes==
Line 7: Line 13:
  
 
Your best bet would be to avoid the autocalc fields entirely if you can. Monitor the two other fields and when they change, have a sheetworker that adds them up to the new value. Then you can refer to the new value in other calculations much easier.
 
Your best bet would be to avoid the autocalc fields entirely if you can. Monitor the two other fields and when they change, have a sheetworker that adds them up to the new value. Then you can refer to the new value in other calculations much easier.
<pre data-language="javascript">
+
<pre data-language="javascript" style="overflow:auto;white-space:pre-wrap;">
 
on("sheet:opened change:stat_a change:stat_b", function() {
 
on("sheet:opened change:stat_a change:stat_b", function() {
 
     getAttrs(["stat_a", "stat_b"], function(values) {
 
     getAttrs(["stat_a", "stat_b"], function(values) {
 
         setAttrs({
 
         setAttrs({
             "foo_modchars": parseInt(values["stat_a"]) || 0 + parseInt(values["stat_b"]) || 0
+
             "foo_modchars": (parseInt(values["stat_a"]) || 0) + (parseInt(values["stat_b"]) || 0)
 
         });
 
         });
 
     });
 
     });
Line 50: Line 56:
 
<pre data-language="javascript">
 
<pre data-language="javascript">
 
setAttrs({
 
setAttrs({
   "foo_modchars": parseInt(values["stat_a"],10) || 0 + parseInt(values["stat_b"],10) || 0
+
   "foo_modchars": (parseInt(values["stat_a"]) || 0) + (parseInt(values["stat_b"]) || 0)
 
});
 
});
 
</pre>
 
</pre>
Line 139: Line 145:
 
</pre>
 
</pre>
  
__FORCETOC__
+
 
 +
==Summing up values based on multiple checkboxes==
 +
{{fpl|10065474/ example}} by [[Richard T.]]
 +
 
 +
<pre style="overflow:auto;white-space:pre-wrap;" data-language="html">
 +
<input readonly input="text" name="attr_combined">
 +
<input type="checkbox" name="attr_part1" value="This">This
 +
<input type="checkbox" name="attr_part2" value="Is A">Is A
 +
<input type="checkbox" name="attr_part3" value="Test">Test
 +
 
 +
<script type="text/worker">
 +
on("change:part1 change:part2 change:part3 sheet:opened", function() {
 +
    getAttrs(["part1", "part2", "part3"], function(values) {
 +
        let operation = values.part1 + values.part2 + values.part3;
 +
        setAttrs({
 +
            "combined": operation
 +
        });
 +
    });
 +
});
 +
</script>
 +
</pre>
 +
 
 +
 
 +
==Migrate Attributes==
 +
Sheetworker to migrate older sheet attributes new ones, such as when a sheet have received a major update, or have renamed an attribute.
 +
 
 +
Essentially it's a script that checks what version a sheet have when it's opened, compares to values in the sheet worker, and performs the updates to the sheet if it's version is outdated compared to the latest version existing in the sheetworker.
 +
 
 +
See [[Sheet_Author_Tips#Sheet_Versioning|Sheet Versioning]] for more about how to have sheetworker that keeps track of the sheet version.
 +
 
 +
===Genefunk 2090 example===
 +
by [[Scott C.]]
 +
{{repo|Roll20/roll20-character-sheets/blob/4c930e6ee3ceae8dd777e483d7ee8c801fad754a/Genefunk%202090/genefunk2090.js#L1788 sourcecode link}}
 +
<pre style="overflow:auto;white-space:pre-wrap;" data-language="javascript">
 +
on('sheet:opened',openSheet);
 +
var openSheet = function(){
 +
    //getSections is a function that I use to get all the repeating section attributes. It iterates through each section and gets all the IDs, and adds those full attribute names to the getArray that is passed to it; baseGet in this case. Once all the sections have been parsed, it calls the callback function passed to it; updateSheet in this case
 +
    getSections(updateSheet,baseGet);
 +
},
 +
updateSheet = function(getArray,sections){
 +
    const setObj = {};
 +
    //getArray will have pretty much every attribute on the sheet in it.
 +
    getAttrs([...getArray,'sheet_version'],(attr)=>{
 +
        //Logic to determine what updates need to be done
 +
        if(!attr.sheet_version){//note that I would not recommend this piece of logic, but I goofed when I first made the sheet and did not store the sheet version number on the sheet
 +
            attr.sheet_version = 0;
 +
        }
 +
        if(attr.sheet_version*1 < 1.1) updateTo1_1(attr,setObj);//First update applied
 +
        if(attr.sheet_version*1 < 1.12) updateTo1_12(attr,setObj);//update 1.12; npc hp attribute change
 +
        set(setObj);
 +
    });
 +
},
 +
//Each individual update function only cares about what updates it's responsible for. And changes to the attributes are added to both the setObj as well as the original attribute object. In this way, later update functions can see what has been done without having to waterfall setAttrs => getAttrs => setAttrs
 +
updateTo1_12 = function(attr,setObj){
 +
    if(attr.character_type === 'npc'){
 +
        setObj.hp = attr.hit_points;
 +
        setObj.hp_max = attr.hit_points_max;
 +
    }
 +
    attr = {...attr,...setObj};
 +
    setObj.sheet_version = 1.12;
 +
},
 +
 
 +
updateTo1_1 = function(attr,setObj){
 +
    let enableItems = Object.keys(attr).filter((a)=>/attack_enable_item/.test(a));
 +
    enableItems.forEach((check)=>{
 +
        let macroName = check.replace(/attack_enable_item/,'attack_macro');
 +
        if(/@/.test(attr[check])){
 +
            setObj[check] = 1;
 +
            setObj[macroName] = `{{d20=[[@{advantage_state} ${attr.character_type === 'pc' ? '+ @{attack_ability} + @{action_proficiency}' : ''} + 0@{attack_bonus}]]}}`;
 +
        }else if(!attr[check]*1){
 +
            setObj[macroName] = ' ';
 +
            setObj[check] = 0;
 +
        }
 +
    });
 +
    setObj.sheet_version = 1.1;
 +
    attr = {...attr,...setObj};
 +
};
 +
</pre>
 +
 
 +
===Migrating attributes from static to repeating section===
 +
 
 +
Ironsworn example of a sheetworker migrating attributes to a repeating section.
 +
 
 +
{{repo|Roll20/roll20-character-sheets/blob/4c930e6ee3ceae8dd777e483d7ee8c801fad754a/Ironsworn/src/app/workers/scripts/vows.js#L12 ironsworn sourcecode}}
 +
 
 +
<pre style="overflow:auto;white-space:pre-wrap;" data-language="javascript">
 +
function fillRepeatingVow (vowNumber) {
 +
  const repeatingAttrs = [
 +
    "vow_name",
 +
    "threat-name",
 +
    `menace-show-button`,
 +
    "menace-show",
 +
    "menace-1",
 +
    "menace-2",
 +
    "menace-3",
 +
    "menace-4",
 +
    "menace-5",
 +
    "menace-6",
 +
    "menace-7",
 +
    "menace-8",
 +
    "menace-9",
 +
    "menace-10",
 +
    "rank",
 +
    "progress_0",
 +
    "progress_1",
 +
    "progress_2",
 +
    "progress_3",
 +
    "progress_4",
 +
    "progress_5",
 +
    "progress_6",
 +
    "progress_7",
 +
    "progress_8",
 +
    "progress_9"
 +
  ];
 +
  const legacyAttrs = [
 +
    `vow${vowNumber}_name`,
 +
    `vow${vowNumber}_threat-name`,
 +
    `vow${vowNumber}_menace-show-button`,
 +
    `vow${vowNumber}_menace-show`,
 +
    `vow${vowNumber}-1-threat`,
 +
    `vow${vowNumber}-2-threat`,
 +
    `vow${vowNumber}-3-threat`,
 +
    `vow${vowNumber}-4-threat`,
 +
    `vow${vowNumber}-5-threat`,
 +
    `vow${vowNumber}-6-threat`,
 +
    `vow${vowNumber}-7-threat`,
 +
    `vow${vowNumber}-8-threat`,
 +
    `vow${vowNumber}-9-threat`,
 +
    `vow${vowNumber}-10-threat`,
 +
    `vow${vowNumber}_rank`,
 +
    `vow${vowNumber}-0`,
 +
    `vow${vowNumber}-1`,
 +
    `vow${vowNumber}-2`,
 +
    `vow${vowNumber}-3`,
 +
    `vow${vowNumber}-4`,
 +
    `vow${vowNumber}-5`,
 +
    `vow${vowNumber}-6`,
 +
    `vow${vowNumber}-7`,
 +
    `vow${vowNumber}-8`,
 +
    `vow${vowNumber}-9`
 +
  ];
 +
  getAttrs(legacyAttrs, function(vow) {
 +
    const newrowid = generateRowID();
 +
    var newAttrs = {};
 +
    repeatingAttrs.forEach(function (attr, index) {
 +
      newAttrs[`repeating_vow_${newrowid}_${attr}`] = vow[legacyAttrs[index]];
 +
    })
 +
    setAttrs(newAttrs);
 +
  });
 +
}
 +
 
 +
on("sheet:opened", function() {
 +
  getAttrs(["vows_migrated"], function(migrationCheck) {
 +
    if (migrationCheck.vows_migrated == true) {
 +
      return true
 +
    } else {
 +
      for (currentVow = 1; currentVow <= 5; currentVow++) {
 +
        fillRepeatingVow(currentVow);
 +
      }
 +
      setAttrs({vows_migrated: true});
 +
    };
 +
  });
 +
});
 +
</pre>
  
 
=Related Pages=
 
=Related Pages=
Line 150: Line 319:
 
* '''{{fpl|8034567/ Sheet Worker Optimization}}''' by [[Scott C.]]
 
* '''{{fpl|8034567/ Sheet Worker Optimization}}''' by [[Scott C.]]
 
* {{fpl|6964447/ How to integrate table of stats into a sheet}} by [[GiGs]]
 
* {{fpl|6964447/ How to integrate table of stats into a sheet}} by [[GiGs]]
 +
* {{forum|post/7664924/multiversal-sheet-worker-generator-this-is-bonkers/ Multiversal Sheetworker Generator}} by [[GiGs]]
 
* [https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps Introduction to JavaScript] - MDN web docs
 
* [https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps Introduction to JavaScript] - MDN web docs
 
* [https://developer.mozilla.org/en-US/docs/Learn/Accessibility/CSS_and_JavaScript#JavaScript JavaScript Best Practices] - MDN web docs
 
* [https://developer.mozilla.org/en-US/docs/Learn/Accessibility/CSS_and_JavaScript#JavaScript JavaScript Best Practices] - MDN web docs

Latest revision as of 03:20, 15 March 2023

Main Article: Sheet Worker Scripts


Short code snippets to be used. See Also: Sheetworker Examples for Non-programmers


Contents

[edit] Examples

[edit] Auto-calculating Attributes

[edit] Example 1

(credit: Rabulias)

Your best bet would be to avoid the autocalc fields entirely if you can. Monitor the two other fields and when they change, have a sheetworker that adds them up to the new value. Then you can refer to the new value in other calculations much easier.

on("sheet:opened change:stat_a change:stat_b", function() {
    getAttrs(["stat_a", "stat_b"], function(values) {
        setAttrs({
            "foo_modchars": (parseInt(values["stat_a"]) || 0) + (parseInt(values["stat_b"]) || 0)
        });
    });
});

[edit] Example 2

(credit: GiGs)

I remember seeing a script someone wrote to allow you to use autocalc fields within sheet workers, but it's just simpler to use Rabulias's approach(see example 1 above).

Add the relevant stats to the on(change:) line, and duplicate the calculation within the sheet worker.

I generally don't put my working in the setattrs call, but before it so i can more easily check it. Something like

on("sheet:opened change:stat_a change:stat_b", function() {
  getAttrs(["stat_a", "stat_b"], function(values) {
      var stat_a = parseInt(values["stat_a"])||0;
      var stat_b = parseInt(values["stat_b"])||0;
      var output = stat_a + stat_b;
      setAttrs({
        "foo_modchars": output
      });
  });
});

[edit] Helper Functions

This section is for useful functions that aren't complete sheet workers, but are useful to use in sheet workers.

[edit] Function: parseValues

(credit: GiGs) Many sheet workers have a bunch of lines like this:

      var stat_a = parseInt(values["stat_a"])||0;
      var stat_b = parseInt(values["stat_b"])||0;

You might also have lines like this:

setAttrs({
   "foo_modchars": (parseInt(values["stat_a"]) || 0) + (parseInt(values["stat_b"]) || 0)
});

It gets tedious typing out all that. With the function below, you would instead write them as:

var stat_a = parseValues(values,"stat_a");
var stat_b = parseValues(values,"stat_b");
setAttrs({
   "foo_modchars": parseValues(values,"stat_a") + parseValues(values,"stat_b")
});

I think that's a lot easier to read. Here's the function:

[edit] parseValues

Place this at the start of your script block, and you'll be able to use it in all your sheet workers.

const parseValues = (values, stat, type='int') => {
     if(type === 'int') return parseInt(values[stat])||0;
     else if(type === 'float') return parseFloat(values[stat])||0;
     else if(type === 'str') return values[stat];
};

By default, it returns an integer. If you call it with a second parameter, it will return either a float or a string:

  • parseValues(values, stat) or parseValues(values, stat, 'int') - returns an integer.
  • parseValues(values, stat,'float') - returns a Float (a number that is not an integer)
  • parseValues(values, stat, 'str') - returns the value as text. (Not really needed!)

This function does handle variable attribute names. If you were in a loop and creating attributes like, "stat" + i it will work fine.

[edit] Reuse of fields for listeners

(credit: Marco G.)

In order to prevent typing the same thing over and over again you can store the fields that you want to use in an array and reuse it for event listeners and getAttrs.

It will looks like this:

      let fields = ["str", "dex", "con"];
      on(fields.map(field => "change:" + field).join(" "), () => {
        getAttrs(fields, (values ) => {
          ....
        });
      });

[edit] Repeating Section

[edit] Example 1

Example of how to edit stats within a repeated section. sourc(Forum)

// this is wrapped inside a repeating section that is called "repeating_psychicabilities"
<input class='hidden' type='text' name='attr_psy_ab_total_finder' readonly />
<input class='hidden' type='text' name='attr_psy_ab_unnat_finder' readonly />
<select name='attr_P_focus' class=''>
    <option value='wp' selected='selected'>Willpower</option>
    <option value='per'>Perception</option>
    <option value='psyniscience'>Psyniscience</option>
</select>
  on('sheet:opened change:repeating_psychicabilities:p_focus', function() {
	getSectionIDs('psychicabilities', function(idarray) {
	  // first get the attribute names for all rows in put in one array
	  const fieldnames = [];
	  idarray.forEach(id => fieldnames.push(`repeating_psychicabilities_${id}_P_focus`));
	  
	  getAttrs(fieldnames, values => {
		// create a variable to hold all the attribute values you re going to create.
		const attr = {};
		// now loop through the rows again
		idarray.forEach(id => {
		  let row = 'repeating_psychicabilities_' + id;
		  let p_focus = values[`${row}_P_focus`] || wp;
		  if (p_focus.length > 3) {
			attr[`${row}_psy_ab_total_finder`] = `@{${p_focus}_total}`;
			attr[`${row}_psy_ab_unnat_finder`] = `@{${p_focus}_unnat}`;
		  }
		  else {
			attr[`${row}_psy_ab_total_finder`] = `@{${p_focus}Total}`;
			attr[`${row}_psy_ab_unnat_finder`] = `@{${p_focus.toUpperCase()}_unnat}`;
		  }
		});
		console.log(values,attr)
		setAttrs(attr);
	  });
	});
  });


[edit] Summing up values based on multiple checkboxes

example(Forum) by Richard T.

<input readonly input="text" name="attr_combined">
<input type="checkbox" name="attr_part1" value="This">This
<input type="checkbox" name="attr_part2" value="Is A">Is A
<input type="checkbox" name="attr_part3" value="Test">Test

<script type="text/worker">
on("change:part1 change:part2 change:part3 sheet:opened", function() {
    getAttrs(["part1", "part2", "part3"], function(values) {
        let operation = values.part1 + values.part2 + values.part3;
        setAttrs({
            "combined": operation
        });
    });
});
</script>


[edit] Migrate Attributes

Sheetworker to migrate older sheet attributes new ones, such as when a sheet have received a major update, or have renamed an attribute.

Essentially it's a script that checks what version a sheet have when it's opened, compares to values in the sheet worker, and performs the updates to the sheet if it's version is outdated compared to the latest version existing in the sheetworker.

See Sheet Versioning for more about how to have sheetworker that keeps track of the sheet version.

[edit] Genefunk 2090 example

by Scott C. sourcecode link

on('sheet:opened',openSheet);
var openSheet = function(){
    //getSections is a function that I use to get all the repeating section attributes. It iterates through each section and gets all the IDs, and adds those full attribute names to the getArray that is passed to it; baseGet in this case. Once all the sections have been parsed, it calls the callback function passed to it; updateSheet in this case
    getSections(updateSheet,baseGet);
},
updateSheet = function(getArray,sections){
    const setObj = {};
    //getArray will have pretty much every attribute on the sheet in it.
    getAttrs([...getArray,'sheet_version'],(attr)=>{
        //Logic to determine what updates need to be done
        if(!attr.sheet_version){//note that I would not recommend this piece of logic, but I goofed when I first made the sheet and did not store the sheet version number on the sheet
            attr.sheet_version = 0;
        }
        if(attr.sheet_version*1 < 1.1) updateTo1_1(attr,setObj);//First update applied
        if(attr.sheet_version*1 < 1.12) updateTo1_12(attr,setObj);//update 1.12; npc hp attribute change
        set(setObj);
    });
},
//Each individual update function only cares about what updates it's responsible for. And changes to the attributes are added to both the setObj as well as the original attribute object. In this way, later update functions can see what has been done without having to waterfall setAttrs => getAttrs => setAttrs
updateTo1_12 = function(attr,setObj){
    if(attr.character_type === 'npc'){
        setObj.hp = attr.hit_points;
        setObj.hp_max = attr.hit_points_max;
    }
    attr = {...attr,...setObj};
    setObj.sheet_version = 1.12;
},

updateTo1_1 = function(attr,setObj){
    let enableItems = Object.keys(attr).filter((a)=>/attack_enable_item/.test(a));
    enableItems.forEach((check)=>{
        let macroName = check.replace(/attack_enable_item/,'attack_macro');
        if(/@/.test(attr[check])){
            setObj[check] = 1;
            setObj[macroName] = `{{d20=[[@{advantage_state} ${attr.character_type === 'pc' ? '+ @{attack_ability} + @{action_proficiency}' : ''} + 0@{attack_bonus}]]}}`;
        }else if(!attr[check]*1){
            setObj[macroName] = ' ';
            setObj[check] = 0;
        }
    });
    setObj.sheet_version = 1.1;
    attr = {...attr,...setObj};
};

[edit] Migrating attributes from static to repeating section

Ironsworn example of a sheetworker migrating attributes to a repeating section.

ironsworn sourcecode

function fillRepeatingVow (vowNumber) {
  const repeatingAttrs = [
    "vow_name",
    "threat-name",
    `menace-show-button`,
    "menace-show",
    "menace-1",
    "menace-2",
    "menace-3",
    "menace-4",
    "menace-5",
    "menace-6",
    "menace-7",
    "menace-8",
    "menace-9",
    "menace-10",
    "rank",
    "progress_0",
    "progress_1",
    "progress_2",
    "progress_3",
    "progress_4",
    "progress_5",
    "progress_6",
    "progress_7",
    "progress_8",
    "progress_9"
  ];
  const legacyAttrs = [
    `vow${vowNumber}_name`,
    `vow${vowNumber}_threat-name`,
    `vow${vowNumber}_menace-show-button`,
    `vow${vowNumber}_menace-show`,
    `vow${vowNumber}-1-threat`,
    `vow${vowNumber}-2-threat`,
    `vow${vowNumber}-3-threat`,
    `vow${vowNumber}-4-threat`,
    `vow${vowNumber}-5-threat`,
    `vow${vowNumber}-6-threat`,
    `vow${vowNumber}-7-threat`,
    `vow${vowNumber}-8-threat`,
    `vow${vowNumber}-9-threat`,
    `vow${vowNumber}-10-threat`,
    `vow${vowNumber}_rank`,
    `vow${vowNumber}-0`,
    `vow${vowNumber}-1`,
    `vow${vowNumber}-2`,
    `vow${vowNumber}-3`,
    `vow${vowNumber}-4`,
    `vow${vowNumber}-5`,
    `vow${vowNumber}-6`,
    `vow${vowNumber}-7`,
    `vow${vowNumber}-8`,
    `vow${vowNumber}-9`
  ];
  getAttrs(legacyAttrs, function(vow) {
    const newrowid = generateRowID();
    var newAttrs = {};
    repeatingAttrs.forEach(function (attr, index) {
      newAttrs[`repeating_vow_${newrowid}_${attr}`] = vow[legacyAttrs[index]];
    })
    setAttrs(newAttrs);
  });
}

on("sheet:opened", function() {
  getAttrs(["vows_migrated"], function(migrationCheck) {
    if (migrationCheck.vows_migrated == true) {
      return true
    } else {
      for (currentVow = 1; currentVow <= 5; currentVow++) {
        fillRepeatingVow(currentVow);
      }
      setAttrs({vows_migrated: true});
    };
  });
}); 

[edit] Related Pages

[edit] See Also