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

Token Bars and eventInfo.previousValue - eventInfo.newValue

1562213185

Edited 1562256352
Wes
Pro
Sheet Author
 Below I have a sheetworker script for calculating either stress or health which are primarily tracked on the sheet with check boxes. I also have an attribute set up for the health and the health_max so that they can be tracked using linked token bar. The script will take a change to the attribute and loop through assigning the proper amount of check boxes toggled to 1. Alternatively if a check box is checked or unchecked it will update the attribute to the new value. <script type="text/worker"> // CALCULATE STRESS - HEALTH - VALUE FROM CHECKBOXES OR ATTRIBUTE const variableAttributes = ["stress","health"]; variableAttributes.forEach(function (variableAttribute) { on("change:" + variableAttribute + " change:" + variableAttribute + "_1 change:" + variableAttribute + "_2 change:" + variableAttribute + "_3 change:" + variableAttribute + "_4 change:" + variableAttribute + "_5 change:" + variableAttribute + "_6 change:" + variableAttribute + "_7 change:" + variableAttribute + "_8 change:" + variableAttribute + "_9 change:" + variableAttribute + "_10 change:" + variableAttribute + "_11 change:" + variableAttribute + "_12", function(eventinfo) { getAttrs([variableAttribute, variableAttribute + "_max", variableAttribute + "_1", variableAttribute + "_2", variableAttribute + "_3", variableAttribute + "_4", variableAttribute + "_5", variableAttribute + "_6", variableAttribute + "_7", variableAttribute + "_8", variableAttribute + "_9", variableAttribute + "_10", variableAttribute + "_11", variableAttribute + "_12" ], function (values) { console.log("%c|*** Character Sheet Calculating Attribute Value Change ***|", "color:Black; background-color: LawnGreen"); console.log(eventinfo.sourceAttribute + " was activated by " + eventinfo.sourceType); console.log("Previous Value: " + eventinfo.previousValue + " changed to a New Value of: " + eventinfo.newValue); console.log(JSON.stringify(eventinfo)); if ( eventinfo.sourceAttribute == variableAttribute ) { let setAttributeValuesAttrs = {}; for(let a=1; a <= values[variableAttribute + "_max"]; a++) { if (a <= eventinfo.newValue) { setAttributeValuesAttrs[variableAttribute+"_"+a] = 1; console.log("Setting Attribute Values: " + variableAttribute + "_" + a + " = 1"); } else { setAttributeValuesAttrs[variableAttribute+"_"+a] = 0; console.log("Setting Attribute Values: " + variableAttribute + "_" + a + " = 0"); } setAttrs(setAttributeValuesAttrs, {silent:true}); } } else { let setAttributeFinalAttrs = {}; const attributeTotal = (parseInt(values[variableAttribute + "_1"], 10)||0) + (parseInt(values[variableAttribute + "_2"], 10)||0) + (parseInt(values[variableAttribute + "_3"], 10)||0) + (parseInt(values[variableAttribute + "_4"], 10)||0) + (parseInt(values[variableAttribute + "_5"], 10)||0) + (parseInt(values[variableAttribute + "_6"], 10)||0) + (parseInt(values[variableAttribute + "_7"], 10)||0) + (parseInt(values[variableAttribute + "_8"], 10)||0) + (parseInt(values[variableAttribute + "_9"], 10)||0) + (parseInt(values[variableAttribute + "_10"], 10)||0) + (parseInt(values[variableAttribute + "_11"], 10)||0) + (parseInt(values[variableAttribute + "_12"], 10)||0); {setAttributeFinalAttrs[variableAttribute] = attributeTotal} console.log("Setting Attribute Values: " + variableAttribute + " = " + attributeTotal ); setAttrs (setAttributeFinalAttrs, {silent:true}) } }); }); }); </script>     It works very well from the character sheet but if you try to change the value of the linked token bar it will return an undefined newValue / previousValue. See console logs below. Change from 6 to 5 from character sheet to @{health} attribute: |*** Character Sheet Calculating Attribute Value Change ***| health was activated by player Previous Value: 6 changed to a New Value of: 5 {"sourceAttribute":"health","sourceType":"player","triggerName":"health","previousValue":"6","newValue":"5"} Change from 5 to 6 from token bar linked to @{health} attribute: |*** Character Sheet Calculating Attribute Value Change ***| health was activated by player Previous Value: undefined changed to a New Value of: undefined {"sourceAttribute":"health","sourceType":"player","triggerName":"health"}     Changing the Value in the linked token bar will change the attribute on the character sheet but since I'm using the eventInfo.newValue in the sheetworker calculations it throws a wrench in my code and it clears all of the check boxes tracking health. Is not having the linked token bar give eventInfo,previousValue & eventInfo.newValue a bug? An oversight? Not Possible? Wes *Edit* Fixed the Formatting of the sheet worker so future readers can see easier. 7/4/19 10:00 AM MST
1562243433

Edited 1562243461
GiGs
Pro
Sheet Author
API Scripter
You should be able to get prev and new values from the attribute linked to the tokenbar, so I'm pretty sure that's not the source of the problem. The formatting of the code above made it hard to see the source of the issue, but copying it out and formatting it revealed that you have a setAtts statement inside your for loop. Never do this. I sugegst restructuring your code so you have just one setAttrs, right at the end of your code, outside the if/else structure. Change this if ( eventinfo.sourceAttribute == variableAttribute ) { let setAttributeValuesAttrs = {}; to  let setAttributeValuesAttrs = {}; if ( eventinfo.sourceAttribute == variableAttribute ) { In the else block, remove let setAttributeFinalAttrs = {}; and replace any  setAttributeFinalAttrs  references with  setAttributeValuesAttrs . Then move this section setAttrs(setAttributeValuesAttrs, { silent: true }); to the  end, after closing the if else block. You don't really have to do that completely, you could have one setAttrs in each if/else block. The important bit - the really important  bit - is moving the setAttrs outside of the for loop. In case this doesnt fix your issue and we have to look in more detail (when I've woken up, lol), can you share the html code for these health and stress checkboxes. it'll make it easier to see what's happening and let us set up our own copy for testing.  By the way, is there a reason you are using checkboxes and not radio buttons? Does each checkbox have its own independent value? If you used radioboxes, the code would be much simpler since you only need to grab one attribute value for the full set of checkboxes. They can be styles to look like checkboxes with css if that's important.
1562243671
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Yep, unfortunately the event object's newvalue and previous value are not dependable when making a change from outside the sheet 
1562247769
GiGs
Pro
Sheet Author
API Scripter
Scott, are you saying that if you make a change to a token bar, that is linked to an attribute on the sheet, the eventinfo values for that attribute aren't reliable? I can't remember being in a situation where I've had to test that, but I naively assumed it would work properly.
1562250283
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Yep, when you change a value on a token for a linked attribute the event that is passed to the sheet it doesn't have the newValue and previousValue properties. The reason I said from outside the sheet, is that there is one more fillip that I'm not sure is still around or if it's been fixed; and that is that when you change an attribute via API the newValue and previousValue are also missing. Unfortunately, I can't test this atm because API scripts are not dependably triggering sheetworkers right now.
1562255985

Edited 1562259161
GiGs
Pro
Sheet Author
API Scripter
Thanks, i had no idea. Wes, here's a tweak to your script that avoids using newValue. You can get the value of the tokenbar attribute from values[variableAttribute], instead of using newValue. I also moved the setAttrs outside of the loop. I'm tempted to streamline it a bit more, but this should do for now. See if this works. const variableAttributes = ["stress", "health"]; variableAttributes.forEach(function (variableAttribute) { on("change:" + variableAttribute + " change:" + variableAttribute + "_1 change:" + variableAttribute + "_2 change:" + variableAttribute + "_3 change:" + variableAttribute + "_4 change:" + variableAttribute + "_5 change:" + variableAttribute + "_6 change:" + variableAttribute + "_7 change:" + variableAttribute + "_8 change:" + variableAttribute + "_9 change:" + variableAttribute + "_10 change:" + variableAttribute + "_11 change:" + variableAttribute + "_12", function (eventinfo) { getAttrs([variableAttribute, variableAttribute + "_max", variableAttribute + "_1", variableAttribute + "_2", variableAttribute + "_3", variableAttribute + "_4", variableAttribute + "_5", variableAttribute + "_6", variableAttribute + "_7", variableAttribute + "_8", variableAttribute + "_9", variableAttribute + "_10", variableAttribute + "_11", variableAttribute + "_12"], function (values) { console.log("%c|*** Character Sheet Calculating Attribute Value Change ***|", "color:Black; background-color: LawnGreen"); console.log(eventinfo.sourceAttribute + " was activated by " + eventinfo.sourceType); console.log("Previous Value: " + eventinfo.previousValue + " changed to a New Value of: " + eventinfo.newValue); console.log(JSON.stringify(eventinfo)); if (eventinfo.sourceAttribute == variableAttribute) { // stress or health token bar change let setAttributeValuesAttrs = {}; const newValue = values[variableAttribute]*1||0; const maxValue = values[variableAttribute + '_max']*1||0; for (let a = 1; a <= maxValue; a++) { if (a <= newValue) { setAttributeValuesAttrs[variableAttribute + "_" + a] = 1; console.log("Setting Attribute Values: " + variableAttribute + a + " = 1"); } else { setAttributeValuesAttrs[variableAttribute + "_" + a] = 0; console.log("Setting Attribute Values: " + variableAttribute + a + " = 0"); } } setAttrs(setAttributeValuesAttrs, {silent: true}); } else {                 // checkbox changes const setAttributeFinalAttrs = {}; const attributeTotal = (parseInt(values[variableAttribute + "_1"], 10) || 0) + (parseInt(values[variableAttribute + "_2"], 10) || 0) + (parseInt(values[variableAttribute + "_3"], 10) || 0) + (parseInt(values[variableAttribute + "_4"], 10) || 0) + (parseInt(values[variableAttribute + "_5"], 10) || 0) + (parseInt(values[variableAttribute + "_6"], 10) || 0) + (parseInt(values[variableAttribute + "_7"], 10) || 0) + (parseInt(values[variableAttribute + "_8"], 10) || 0) + (parseInt(values[variableAttribute + "_9"], 10) || 0) + (parseInt(values[variableAttribute + "_10"], 10) || 0) + (parseInt(values[variableAttribute + "_11"], 10) || 0) + (parseInt(values[variableAttribute + "_12"], 10) || 0); setAttributeFinalAttrs[variableAttribute] = attributeTotal; console.log("Setting Attribute Values: " + variableAttribute + " = " + attributeTotal); setAttrs(setAttributeFinalAttrs, {silent: true}); } }); }); });
1562258176

Edited 1562258281
Wes
Pro
Sheet Author
First off "fillip" -- From&nbsp; <a href="https://en.wiktionary.org/wiki/fillip" rel="nofollow">https://en.wiktionary.org/wiki/fillip</a> Etymology: From&nbsp; Middle English &nbsp; philippe ,&nbsp; filippen &nbsp; ( “ to make a sound with right forefinger and thumb, snap ” ) . Origin uncertain. Probably an alteration of&nbsp; Middle English &nbsp; flappen &nbsp; ( “ to hit, slap, clap, applaud ” ) . More at&nbsp; flap . At first, the literal sense was extended to “something of small importance; a trifle” (“the rest is not worth a fillip with the finger”), then to a short space of time (the time it would take to flick a finger, as in “the tortoise...in a fillip of the finger was down in the gardens of Riu Gu”). Only in the 18th and 19th centuries did its current usage, as encouragement or stimulus, come to dominate. Thank you Scott C. for making me look! Second: Thank you GiGs for the code corrections I'm currently working on making those changes. The reason for the check boxes was two fold; It was "Borrowed" from another project I'm working on where the checkboxes are going to be arranged in blocks and not very linear, I wanted to let the user decide which check boxes to mark or unmark as they preferred. Secondly it was a chance for me to push my javascript and learn more.&nbsp; I haven't taken any classes and I have been absorbing what those of you with more knowledge have been putting on the forums. I found this to be an opportunity to take something I had seen and use it in a practical situation for me to learn how the code is working and make me think logically about it. This is actually for a couple of projects I'm pursuing, an Alien RPG Character sheet and a Tiny D6 games Character sheet. Both systems have small health or stress&nbsp; pools of 10-12 check marks on the paper character sheets. It just feels right to emulate that in the digital sheet. TLDR: I can use Radio Inputs, but I was trying to learn more Java Script! PS: I was typing this as Gigs was posting.... Thanks for that update. I'll check it out now.
1562259232
GiGs
Pro
Sheet Author
API Scripter
I just this minute noticed a typo in the code i posted and fixed it before i saw you had posted.
1562259361
Wes
Pro
Sheet Author
The updated code works as expected. Thank you. I follow that you created variables that contained the new value and the max value, and then moved the setAttr to outside the for loop. I guess that just leaves the question of whether or not the linked attribute from the token should have previousValue and newValue as part of the eventInfo.
1562260336

Edited 1562262738
GiGs
Pro
Sheet Author
API Scripter
The only reason you need to use eventinfo in this version of the script is for checking whether the change event was stress or health, and not stress_1, stress_2, etc. You can delete all references to newvalue and prevvalue, I only left them in in case you wanted them for logging. I made an alternate form of the sheet worker if you're feeling bold and want to test it. It's using a technique I havent used before ( the array(12).fill.... etc line). and I'm curious if it works properly (I havent got a character sheet set up to test it) :) I like to compress all those repeated attribute_1, attribute_2.... attribute_12 values in change and getattrs lines, and if this works for you, its a technique that could be useful whenever you have multiple checkboxes or attributes named similarly, to reduce the amount of repetitive code (and typing). // CALCULATE STRESS - HEALTH - VALUE FROM CHECKBOXES OR ATTRIBUTE const variableAttributes = ["stress", "health"]; variableAttributes.forEach(function (variableAttribute) { // create an array of the checkbox names. the array(12) sets how many checkboxes there are - change to match. const variableAttributeChecks = Array(12).fill().map((_, index) =&gt; `${variableAttribute}_${index +1}`); on(`change:${variableAttribute} change:${variableAttributeChecks.join(' change:')}`, function (eventinfo) { getAttrs(variableAttributeChecks.concat([variableAttribute, variableAttribute + "_max"]), function (values) { console.log("%c|*** Character Sheet Calculating Attribute Value Change ***|", "color:Black; background-color: LawnGreen"); console.log(eventinfo.sourceAttribute + " was activated by " + eventinfo.sourceType); console.log("Previous Value: " + eventinfo.previousValue + " changed to a New Value of: " + eventinfo.newValue); console.log(JSON.stringify(eventinfo)); console.log('checkbox names: ' + variableAttributeChecks.join(', ')); if (eventinfo.sourceAttribute == variableAttribute) { // stress or health token bar change const newValue = values[variableAttribute]*1||0; const setAttributeValuesAttrs = variableAttributeChecks.reduce((final,item,index) =&gt; { final[item] = (index &lt; newValue ? 1: 0); // index will be 0 to 11, so we use &lt; instead of &lt;= console.log(`Setting Attribute Values: ${item} = ${index &lt; newValue ? 1: 0}`); return final; },{}); setAttrs(setAttributeValuesAttrs, {silent: true}); } else { const attributeTotal = variableAttributeChecks.reduce((total,item) =&gt; total + values[item] *1||0, 0); const setAttributeFinalAttrs = {}; setAttributeFinalAttrs[variableAttribute] = attributeTotal; console.log("Setting Attribute Values: " + attributeTotal); setAttrs(setAttributeFinalAttrs, {silent: true}); } }); }); }); Word of warning: i often get the syntax of reduce functions wrong on first attempt, lol, hope this works properly.
1562262792

Edited 1562262909
Wes
Pro
Sheet Author
I tried it out and it comes up with this console log. The correct values, but with improper syntax? |*** Character Sheet Calculating Attribute Value Change ***| health_6 was activated by player Previous Value: 1 changed to a New Value of: 0 {"sourceAttribute":"health_6","sourceType":"player","triggerName":"health_6","previousValue":"1","newValue":"0"} checkbox names: health_1, health_2, health_3, health_4, health_5, health_6, health_7, health_8, health_9, health_10, health_11, health_12 Setting Attribute Values: health_111110000000 I pm'd&nbsp; you the html and css if you want to try it out GiGs.
1562263015
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Gigs, why array.fill? What does that give you that .map doesn't? Also, doesn't .fill require the first argument?
1562263025
GiGs
Pro
Sheet Author
API Scripter
As expected there was a minor syntax error in one of the reduce functions, hehe, but I've fixed it and tested the previous function. It works, yay! One thing I noticed in both versions, with respect to your comment: The reason for the check boxes was two fold; It was "Borrowed" from another project I'm working on where the checkboxes are going to be arranged in blocks and not very linear, I wanted to let the user decide which check boxes to mark or unmark as they preferred.&nbsp; There's a problem here. If the user checks, say, box 1, 3, 5, 7, 9 and 11, they'll have a value of 6. If they then alter the token value to 7, the checks will be completely overwritten: now 1, 2, 3, 4, 5, 6, and 7 will be checked, and the previously checked 9 and 11 will be unchecked. Now, it is possible to rewrite either function to preserve the boxes the user has checked. The question is; is it that important? And if not, to be honest you'd be better off with radio buttons. the code is simpler.
1562263523
GiGs
Pro
Sheet Author
API Scripter
Scott C. said: Gigs, why array.fill? What does that give you that .map doesn't? Also, doesn't .fill require the first argument? Array.fill is to make sure the array has iterable values. If you just use array(12), for example, you get an array with&nbsp; very &nbsp;undefined values, and map and other iterative functions won't work properly, at least according the reading I've done. Fill() does actually have a value - the value is undefined . As I understand it, this creates an array with properly defined undefined values (ahhh, javascript), which you can then use map to iterate over and replace the undefined values with actual values.
1562265319
Wes
Pro
Sheet Author
GiGs said: One thing I noticed in both versions, with respect to your comment: The reason for the check boxes was two fold; It was "Borrowed" from another project I'm working on where the checkboxes are going to be arranged in blocks and not very linear, I wanted to let the user decide which check boxes to mark or unmark as they preferred.&nbsp; There's a problem here. If the user checks, say, box 1, 3, 5, 7, 9 and 11, they'll have a value of 6. If they then alter the token value to 7, the checks will be completely overwritten: now 1, 2, 3, 4, 5, 6, and 7 will be checked, and the previously checked 9 and 11 will be unchecked. Now, it is possible to rewrite either function to preserve the boxes the user has checked. The question is; is it that important? And if not, to be honest you'd be better off with radio buttons. the code is simpler. &nbsp;&nbsp;&nbsp;&nbsp;The borrowed from Project is for a long time passion project "Battletech: A game of Armored Combat" where the character sheet is more of a vehicle record sheet that has multiple Armor check boxes. In this situation there wont be a visible Attribute for changing just lots of check boxes so that scenario shouldn't come into play. If it ever does then "Let the dice fall where they may, sort of situation." &nbsp; &nbsp; Also, yeah it would be faster to have just he attributes but I have fond memories of counting off boxes while cursing my competitors luck at rolling to hit the same location 3 times in a row..... Thanks for all the help with this I appreciate it!
1562265567
GiGs
Pro
Sheet Author
API Scripter
Wes said: The borrowed from Project is for a long time passion project "Battletech: A game of Armored Combat" where the character sheet is more of a vehicle record sheet that has multiple Armor check boxes. In this situation there wont be a visible Attribute for changing just lots of check boxes so that scenario shouldn't come into play. If it ever does then "Let the dice fall where they may, sort of situation." &nbsp; &nbsp; Also, yeah it would be faster to have just he attributes but I have fond memories of counting off boxes while cursing my competitors luck at rolling to hit the same location 3 times in a row..... Thanks for all the help with this I appreciate it! That makes sense. And you're welcome. :)