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

[SheetWorker][TAS][The Aaron Sheet] Read the value of a macro in a sheet worker

1591079378

Edited 1591079489
Peter B.
Pro
Sheet Author
Hello Roll20 experts. Context for the problem I am trying to make a field that shows a player how many proficiency points they have left to spend. I have an input field where the player is suggested to put a macro to calculate a value. I would like to read the value outputted by the macro in a sheet worker, to do my additional calculations. The reason why I am trying to do this with a sheetworker, is because the character sheet is already loaded with UI-thread calculations, making the entire sheet pretty slow. Therefore I am trying to focus on keeping calculations in the backend thread, thus making the sheet more responsive. The calculations also include values from repeating fields, so that also require sheet workers. The HTML looks something like this (shorted down for clarity): <!-- The input field for the macro --> <input type="text" name="attr_profslotstotal" title="@{profslotstotal}" class="sheet-display" value="4+floor(@{level-class1}/3)+@{intlang}"/> <!-- The disabled input field for the macro's calculated value --> <input type="text" name="attr_profslotstotalcalc" value="[[@{profslotstotal}]]" style="display: none;" disabled /> <!-- The output field for the remaining points --> <input type="text" name="attr_profslotsremain" title="@{profslotsremain}" class="sheet-short" placeholder="Slots" value="0" readonly/> <fieldset name="repeating_profs" title="@{profs}" class="repeating_profs"> <table class="sheet-table-white">     <tr> <td><input type="text" name="attr_profname" title="@{repeating_profs_$X_profname}" placeholder="Name of NonWepProf"></td> <td><input type="number" name="attr_profslots" title="@{repeating_profs_$X_profslots}" class="sheet-short" placeholder="Slots"></td> <td><input type="text" name="attr_profstatnum" title="@{repeating_profs_$X_profstatnum}" placeholder="@{abilityScore}" class="sheet-maxtext"></td> <td>±</td> <td><input type="text" name="attr_profmod" title="@{repeating_profs_$X_profmod}" value="0" class="sheet-short"></td> <td><button type="roll" name="roll_Prof" title="%{repeating_profs_$X_Prof}" value="/roll [[1d20cs<1cf>20]]≤[[(@{profstatnum})+(@{profmod})]] @{profname}"></button></td>     </tr> </table> </fieldset> Here is the sheet worker: on('change:repeating_profs remove:repeating_profs change:profslotstotalcalc', function(){ TAS.repeating('profs') .attrs('profslotsremain','profslotstotalcalc') .fields('profslots') .reduce(function(m,r){ m.profslots+=(r.I.profslots); return m; },{profslots:0, desc: []},function(m,r,a){ a.profslotsremain=(a.I.profslotstotalcalc-m.profslots); }) .execute(); }); My plan was to have a single disabled field, calculated on the UI-thread, to calculate the value of the macro. This is the field profslotstotalcalc . Then I would listen to that field for changes with the sheet worker. Thus whenever the player grew enough levels for the value in the macro to change, then the calculated field would update its value, and this would trigger the sheet worker. The problem narrowed down After a lot of trial and error, I realized that the profslotstotalcalc  field cannot be read from a sheet worker. Apparently, fields that are marked with disabled  and thus auto-calculating, is not placed in Attributes & Abilities  section of the sheet. This means that a sheet worker, cannot read the value of the field and therefore always reads the field to be empty or 0 depending on how the field is read. Solutions? Does anyone have a solution for reading the calculated value of a macro with a sheet worker? I know I could turn the calculations around, so that I sum all the repeating fields to a hidden field (just like the profslotstotalcalc  field) and then calculate the remaining points with a disabled auto-calculating field. However I would prefer to keep as much logic as possible in the sheet worker if possible, so any solution that can eliminate the usage of a hidden disabled field is highly appreciated.
1591092512
Andreas J.
Forum Champion
Sheet Author
Translator
Pretty sure a solution is to replace the autocalc section with a sheetworker.
1591106650

Edited 1591109470
GiGs
Pro
Sheet Author
API Scripter
Peter B. said: The problem narrowed down After a lot of trial and error, I realized that the profslotstotalcalc  field cannot be read from a sheet worker. Apparently, fields that are marked with disabled  and thus auto-calculating, is not placed in Attributes & Abilities  section of the sheet. This means that a sheet worker, cannot read the value of the field and therefore always reads the field to be empty or 0 depending on how the field is read. Before talking about your specific problem, I want to address this misconception. The Attributes section is a legacy feature, and is largely obsolete when you are using a character sheet. There are many kinds of attributes that will never show up there, and you should not use their presence or absence from that section in troubleshooting. Autocalc fields (those with disabled=true) dont show up there, because they aren't normal attributes: they cannot be edited by the user, and if they were on the attributes tab, a user could edit them and delete them. You dont want that to happen! Likewise sheet users cant read autocalc fields - but its not because they aren't on the attributes tab. It's because they are autocalc fields.  There are many attributes that do not appear on that tab that sheet workers can use fine - any attributes in a repeating section, for example.  As Andreas says, the proper way to incorporate an autocalc field in a sheet worker, is to replace it with a sheet worker. This is generally good advice - replace every autocalc field that is used by other attributes with sheet workers. 
1591127457
Peter B.
Pro
Sheet Author
GiGs said: Peter B. said: The problem narrowed down After a lot of trial and error, I realized that the profslotstotalcalc  field cannot be read from a sheet worker. Apparently, fields that are marked with disabled  and thus auto-calculating, is not placed in Attributes & Abilities  section of the sheet. This means that a sheet worker, cannot read the value of the field and therefore always reads the field to be empty or 0 depending on how the field is read. Before talking about your specific problem, I want to address this misconception. The Attributes section is a legacy feature, and is largely obsolete when you are using a character sheet. There are many kinds of attributes that will never show up there, and you should not use their presence or absence from that section in troubleshooting. Autocalc fields (those with disabled=true) dont show up there, because they aren't normal attributes: they cannot be edited by the user, and if they were on the attributes tab, a user could edit them and delete them. You dont want that to happen! Likewise sheet users cant read autocalc fields - but its not because they aren't on the attributes tab. It's because they are autocalc fields.  There are many attributes that do not appear on that tab that sheet workers can use fine - any attributes in a repeating section, for example.  As Andreas says, the proper way to incorporate an autocalc field in a sheet worker, is to replace it with a sheet worker. This is generally good advice - replace every autocalc field that is used by other attributes with sheet workers.  Thank you for your tip. Replacing Autocalc fields with a sheet worker is exactly what I am trying to do! I realize now I was being unclear about my problem. I was using the Autocalc field as a crutch for my sheet worker, because I do not know how to make a sheet worker read the value of a macro. The actual problem consists of this: In the above example I have the macro for proficiency increments: <input type="text" name="attr_profslotstotal" title="@{profslotstotal}" class="sheet-display" value="4+floor(@{level-class1}/3)+@{intlang}"/> This include the default value which is: 4+floor(@{level-class1}/3)+@{intlang} Normally I would find the total value of the above statement by wrapping it in square brackets like this: [[4+floor(@{level-class1}/3)+@{intlang}]] This could be in the chat or an /em message or the likes. At level 3, with a bonus of +2 from intellect this would yield: 4+floor(3/3)+2 == 4+1+2 == 7 But how do I get the value of 7 from the above statement in a sheetworker? I want the users to be able to define custom macros for point progression, and have the sheet worker track whenever the sum of the value changes? I hope my problem is more clear now, and I hope you have an answer. I don't believe I am the only one to ask this question :)
1591130499
GiGs
Pro
Sheet Author
API Scripter
When you start using sheet workers, you end up having to create more sheet workers. You need to move the calculation for  profslotstotal into a sheet worker. If any of the attributes used in the calculation are themselves autocalc fields, you need to replace them with sheet workers. And so on back until no autocalc attributes remain in the chain of attributes leading to this calculation. That said, I'm not following what you mean when you say you want the sheet worker to read the values of a macro. A macro is something you print to chat and produces some output there. You never use its result in a sheet worker. If you just want to replace the profslotstotal calculation with a sheet worker, that's easy enough: on('change:level-class1  change:intlang',  function() {     getAttrs(['level-class1', 'intlang'], function(values) {         let level = parseInt(values['level-class']) || 0;         let intlang = parseInt(values['intlang']) || 0;         let profslotstotal = level + intlang;         setAttrs({profslotstotal});     }); }); If either intlang or level-class are themselves autoclacs, they'd need replacing with sheet workers. If this isnt what you need, can you describe in more detail what you're trying to achieve?
1591165935
Peter B.
Pro
Sheet Author
GiGs said: When you start using sheet workers, you end up having to create more sheet workers. You need to move the calculation for  profslotstotal into a sheet worker. If any of the attributes used in the calculation are themselves autocalc fields, you need to replace them with sheet workers. And so on back until no autocalc attributes remain in the chain of attributes leading to this calculation. Clear up some misunderstandings Okay. Let's set some ground rules: Forget about Autocalc fields. It was an idea that I had, and it turned out not to work! Do not spend more time on this I realized that my use of the word "Macro" have been creating some confusion. So lets use the word Math-expressions . With a Math-expression I mean any form of calculation that can be condensed down to a single number in a /roll message or a /em message by wrapping it in [[ ]]. I consider the following math expressions 5 // The single number 5 I consider a math expression. 4+4 // Here two numbers are added together. A math expression @{intlang}+4 // The value of an attribute and a static number, added together. A math expression 4+floor(@{level-class1}/3) // The value of an attribute, divided by 3 and floored, and added with 4. A math expression What I am trying to achieve In the Rogue skills section, there is the following functionality: In the rogue section seen here, there is this handy field that tells the player how many points they have left to spend, based on a total calculated from the inputted Math-expression (the sheet calls it a macro, that is why I called it a macro). Here the player can insert any form math-expression to encapsulate their thief point progression. In the rogue section the value in the Remaining points field, is achieve by usage of an Autocalc field . What I am trying to achieve is a similar functionality, but without using an Auocalc field . I want it all to be done in a sheet worker . What I am trying to achieve I am trying to create the exact same functionality as with the rogue section, but without using an Autocalc field . I want it all done in a sheet worker. Why a math-expression input is required I need the user to be able to input a math-expression because even though there are standard progressions for Weapon and Nonweapon proficiencies this is not dynamic enough for the player base. Players might be playing a class that is not standard, such as the Pscionic. A class I know nothing about. Players might be playing a custom class that they made up themselves. Players might be playing a special version of a class, a kit, that either has less or more proficiency slots than the standard progression. Players might allow the extra slots gained from intellect, to be used for weapon proficiencies. I have heard AquaAlex talk of such a rule, used by players Players might be playing a multi-class / dual-class thus always looking at @{level-class1} will not be a flexible solution. In the character sheet, players can track up to 5 different classes This is why the input field must allow for a math expression! The Sheetworker I dream of What I want the sheet worker to do, is to read the input field that holds the math-expression, calculate the math-expression, just like it is done in the chat, in an /em or /roll message, and then do further calculations on the input. I want it to do something like this: on('change:profslotstotal ',  function() {     getAttrs(['profslotstotal'], function(values) {         let rawString = values['profslotstotal']; // The value rawString contains the math-expression: '4+floor(@{level-class1}/3)+@{intlang}'         let calculated = [[rawString]]; // Calculates the math-expression into a value, ie. 7. Just like in the chat         let profslotsremain = calculated; // Sets attribute profslotsremain to the calculated value (subtracking of spent points not included for brevity)         setAttrs({profslotsremain}); // Sets the attribute out to the     }); }); Is it in anyway possible to do this? Is there a way for a sheet worker to calculate the value of a math-expression just like it is done in line 4: let calculated = [[rawString]];
1591185117

Edited 1591185170
GiGs
Pro
Sheet Author
API Scripter
This is possible, but it's tricky. There are two ways that spring to mind, which I'll call the Good Way, and the Bad Way. Not that I'm judging or anything.  On reflection, while writing this post, I realised there's also a Third Way, which might actually be the best. it's certainly the simplest. The Third Way Abandon the sheet worker approach. Do everything with autocalc fields. Is there a reason you need to use a sheet worker? The Bad Way Have an onchange event that triggers when the macro changes, gets the macro text, and then breaks it apart to extra the attribute names, and grabs them from the sheet. and then performs the calculation. Pseudo code to illustrit this process: On(change:  profslotstotal)     getAttrs( profslotstotal)         function to extra an array of attribute names from this string         you can do this by extracting everything between @{ and } and putting them in an array         let statnames = the array of stats got from above         getAttrs([statnames]             get the profstotal, and do a massive replace operation:                 replace @{} attaributes with their calculated values from statnames                 replace roll20 functions with javascriot equivalents (eg. floor() becomes Math.floor()             now you have a calculation, get the value             setAtrrs(update the sheet) This is theoretically possible, but extremely laborious and very prone to error. If you want to create a function like this, you are likely on your own. That's the reason I used pseudo code here and not actual code - it'll be a pain to write. The Good Way redesign the html of the sheet to craerte a "macro designer" section, which force users to enter data in a way that you can use easily in the sheet worker. Do NOT allow them to enter this " 4+floor(@{level-class1}/3)+@{intlang}" Instead, add a section which lets them enter the individual parts separately. For instance (very simple example to illustrate the principle) it might have <input name="attr_macro_base"> <input name="attr_macro_level"> <input name="attr_macro_divider"> <input name="attr_macro_bonus"> Players enter the attribute names (without the @{} parts) and any numbers there. So a player might set  base = 4, macro_level =  level-class1 , macro_divider = 3, macro_bonus=  intlang Then in your sheet worker, you collect the attributes on('change:macro_base change:macro_level  change:macro_level_divider change:macro_level_bonus ',  function() {     getAttrs(['macro_base' 'macro_level', 'macro_divider', 'macro_bonus'], function(values) {         let base= parseInt(values['macro_base']) || 0;         let level= parseInt(values['macro_level']) || 0;         let divider= parseInt(values['macro_divider']) || 0;         let bonus= parseInt(values['macro_bonus']) || 0;         let profslotstotal = base + Math.floor(level / divider) + bonus;         setAttrs({profslotstotal});     }); }); Now this specific example only covers a very specific way to build that formula. You'd need to work out every possible way a progression could be built, and build a macro designer that supports it.  The advantage of this approach:  it's actually possible. And its very robust once its built. The Bad Way is  theoretically possible, but its very tricky to design and very prone to error. The disadvantage of the approach:  you have to change our mindset on how to do it. You have to think about what formulas you are willing to support. But in practice, there cant be that many variations. It'll be possible to work them all out and build a macro designer that supports them.  And once you have that, you can design the sheet worker. Finally, you can also construct the macro string in the function too, since you are grabbing the attribute names. So players dont have to build it manually and risk syntax errors.  In Conclusion I hope this illustrates the options well. What you want to do doesnt have an easy solution. 
1591193722
Peter B.
Pro
Sheet Author
GiGs said: The Third Way Abandon the sheet worker approach. Do everything with autocalc fields. Is there a reason you need to use a sheet worker? I it was in the back of my mind to use an Autocalc field as a backup. There is no reason that the solution need  a sheet worker. I was persuing a sheetworker because the sheet today already is a bit slow to load and such, so cutting down on the Autocalc fields and building new stuff with sheet workers was preferable. I will go with this solution, simply to get the feature done for now. Maybe someone else will want to tackle the problem of creating a more sheet worker friendly version.
1591194051
GiGs
Pro
Sheet Author
API Scripter
I think the goal you've set: "allow players to enter whatever formula they want" is very tricky, but it would be a manageable task if you set some constraints on the kind of stuff they can enter. Which you can do by designing the kind of interface I suggest, but it does take a bit of planning and work. Getting away from autocalcs would be better in the long term, but if it works for now, might as well use it.
1591203123
Peter B.
Pro
Sheet Author
GiGs said: I think the goal you've set: "allow players to enter whatever formula they want" is very tricky, but it would be a manageable task if you set some constraints on the kind of stuff they can enter. Which you can do by designing the kind of interface I suggest, but it does take a bit of planning and work. Getting away from autocalcs would be better in the long term, but if it works for now, might as well use it. Exactly my conclusion. Thank you for keeping with me on this problem :)