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 .
×

[HELP] Custom character math syntax?

So, I'm building a character sheet for a system I'm creating for roll20 using the fact that roll20 is a virtual tabletop, thus I can use the computer to do some crazy specific nutso calculations so the player/gm doesn't have to. Preface done, I need to know if it's possible/how to do exponents with the custom character stuffs. Yes, I do mean arbitrary exponential calculations. I also noticed that there is no json tab, which means that there are some sneaky macros involved the the custom character sheet creation thing. is it possible to do exponential calculations? or are the devs going to have to add that in? thank you!
1482554271
Lithl
Pro
Sheet Author
API Scripter
There is no exponent operator or function available to autocalc attributes or macros/abilities. However, you can of course multiply an attribute some specified number of times (for integer exponents), and there's even a method of approximating fractional exponents using the available arithmetic, which you can find in  the CSS Wizardry thread . However, since that post was created, sheet worker scripts have been added, which opens up the option of creating a snippet of JavaScript to calculate the exponent, spitting out the value into an attribute you can reference. Here's a simple example: <input type="number" name="attr_a" value="1"> <input type="number" name="attr_b" value="1"> <input type="number" name="attr_c" readonly="readonly"> <button name="roll_example" value="/r @{c}d10"></button> <script type="text/worker"> on('change:a change:b sheet:opened', function() { getAttrs(['a', 'b'], function(values) { setAttrs({ c: Math.pow(parseFloat(values.a) || 1, parseFloat(values.b) || 1) }); }); }); </script>
Actually, there is an exponential opperand, it's "**" nice and simple. and works.... seemingly flawlessly, doesn't seem to be throwing weird things like complex numbers at me, which is a good thing. And it seems that it can even handle non integer values for all ranges actually.
okay, I do have a question though, is it possible for me to say, throw in a script and simply have one function that does some math and whenever I need to do that calculation, I can simply pass in the arguments that I need?
1482727729

Edited 1482796339
Lithl
Pro
Sheet Author
API Scripter
Raxmo said: okay, I do have a question though, is it possible for me to say, throw in a script and simply have one function that does some math and whenever I need to do that calculation, I can simply pass in the arguments that I need? A sheet worker script can have arbitrary functions that you can access normally, but the entry point will always  be the callback parameter to the on() function. You can't call your arbitrary function from the HTML, for example. Also, thanks for the tip on the exponent operator. I didn't think to try the Python syntax for it. I'm gonna go update the Math-implementation post.
woot! I'm helping!^-^ Okay, so I can't save time when using the script by writing one script and calling it several times, passing in whatever values I need. so it looks like I'll be doing a LOT of copy pasting
1482796727
Lithl
Pro
Sheet Author
API Scripter
What are you trying to accomplish? There may be a simpler way that someone else can help you come up with. =) And you can certainly recursively call the callback function. For example: function myCallback(eventInfo, myParameter1, myParameter2) { // only eventInfo will have a value when the event first fires // do some stuff if (/* we should recursively call this function */) { myCallback(eventInfo, valueForParameter1, valueForParameter2); } } on('sheet:opened', myCallback);
huh.... okay, so, the thing that i'm trying to do is to use a function to recursively modify attributes as I need to. More specifically, I'm using the functions to do some crazy maths to automatically calculate stat values and skill values based off of XP put in each skill. and I'd like to not write out 50+ scripts for all the skills and such
1482830083
Lithl
Pro
Sheet Author
API Scripter
Could you be more specific?
okay, I have a formula that I want to use multiple times. I want to be able to recursively call that function with new arguments for each skill that I have. I guess I could do a for loop and do the calculations for the entire sheet every time anything changes, but that seems horribly innefficient. Mainly, can I pass in attributes? like say I have something like this: function calculation(eventInfo, thing1, thing2){ thing2: thing1 if(/*condition*/){ calculate(eventInfo, thing1, thing2) } } on('sheet:opened', calculation(,'attr_str','attr_str-max')
1482874300
Lithl
Pro
Sheet Author
API Scripter
That's not really any more specific than what you had said previously in the thread. Please tell us what you are trying to accomplish , and we can help you better.
um... well, I don't really know how I can better explain myself >.< What I'm drying to do is use scripts to modify 50 individual values that would take an argument without having to write the same script 50 times with minor differences for each case. like, can I create a "master" script and use helper scripts throughout the file that passes in the necessary information? like, say the example you gave, can I use that to throw in attribute values arbitrarily? or will I have to copy and paste the same script 50 times then modify the arguments? for instance, if I say make a script that calculates the value of a skill based off of skill points put into it, can I have one script/function that does that, and then call that function with whatever arguments there are for each skill?
1482900372
Lithl
Pro
Sheet Author
API Scripter
You seem to be dancing around telling us how the script is really supposed to work, which makes it very difficult to conceptualize what you need. How about this: Let's start with two skills (or whatever). Pick two actual  skills from the system you're trying to write a worker for, and tell me the game rules surrounding the calculation you're trying to perform.
Actually, I have a link to the google sheets version of the system, I think that'll explain what I'm trying to do MUCH better than I can do with my own words (due to the fact that I'm apparently still terrible at doing that) so, here's the link to the sheet:  The spreadsheet So, for each skill, I want to pass the xp value into the function and have the function change the value of the level of that precise skill, so, for say Hauling, I'd pass hauling-xp into the function and the function will change hauling-lvl with minimal scripting, something like: on('change:hauling-xp', calculate(hauling-xp,hauling-lvl)); on('change:athletics-xp', calculate(athletics-xp, athletics-lvl)); function calculate(a,b){ getAttrs(function(values){ setAttrs({ b: math_stuffs_here }); }); });
1482991930

Edited 1482992030
Lithl
Pro
Sheet Author
API Scripter
// CDF/ERF implementation from <a href="http://stackoverflow.com/a/14873282/386178" rel="nofollow">http://stackoverflow.com/a/14873282/386178</a> function cdf(x, mean, variance) { return 0.5 * (1 + erf((x - mean) / Math.sqrt(2 * variance))); } function erf(x) { var sign = Math.sign(x), a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741, a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911, t, y; x = Math.abs(x); t = 1 / (1 + p * x); y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return sign * y; } function skillXpChanged(governingAttrName) { return function(eventInfo) { var xpAttrName = eventInfo.sourceAttribute, attrName = xpAttrName.substring(0, xpAttrName.lastIndexOf('-')), lvlAttrName = attrName + '-lvl', gthAttrName = attrName + '-gth'; getAttrs([governingAttrName, gthAttrName, xpAttrName], function(values) { var attrs = {}; attrs[lvlAttrName] = Math.round(cdf(parseInt(values[xpAttrName]), 100 - parseInt(values[governingAttrName]) - parseInt(values[gthAttrName]), 100) * 200); setAttrs(attrs); }); } } on('change:aggressive-combat-xp change:clashing-defense-xp change:athletics-xp ' + 'change:breaching-xp change:hauling-xp change:initiative-xp', skillXpChanged('strength-lvl')); on('change:eroding-combat-xp change:lasting-defense-xp change:drinking-xp ' + 'change:endurance-xp change:immunity-xp change:gait-xp', skillXpChanged('constitution-lvl')); on('change:counter-combat-xp change:slippery-defense-xp change:acrobatics-xp ' + 'change:stealth-xp change:thievery-xp change:tempo-xp', skillXpChanged('dexterity-lvl')); on('change:miming-combat-xp change:studied-defense-xp change:archana-xp ' + 'change:insight-xp change:literacy-xp change:white-xp', skillXpChanged('wisdom-lvl')); on('change:strategic-combat-xp change:predictive-defense-xp change:alchemy-xp ' + 'change:analysis-xp change:comprehension-xp change:problem-solving-xp', skillXpChanged('intelligence-lvl')); on('change:precise-combat-xp change:reflexive-defense-xp change:perception-xp ' + 'change:sorcery-xp change:recollection-xp change:reaction-xp', skillXpChanged('focus-lvl')); on('change:divinity-xp change:gambling-xp change:intuition-xp ' + 'change:persuasion-xp change:religion-xp change:bravery-xp', skillXpChanged('faith-lvl')); on('change:strength-xp change:constitution-xp change:dexterity-xp change:wisdom-xp ' + 'change:intelligence-xp change:focus-xp change:faith-xp', function(eventInfo) { var xpAttrName = eventInfo.sourceAttribute, attrName = xpAttrName.substring(0, xpAttrName.lastIndexOf('-')), lvlAttrName = attrName + '-lvl', gthAttrName = attrName + '-gth', bonusAttrName = attrName + '-bonus'; getAttrs([gthAttrName, xpAttrName], function(values) { var attrs = {}; attrs[lvlAttrName] = Math.round(cdf(parseInt(values[xpAttrName]), -parseInt(values[gthAttrName]), 100) * 200) - parseInt(bonusAttrName); setAttrs(attrs); }); }); The above code is assuming the attribute names are all lower-case (eventInfo.sourceAttribute returns the name all lower-case, but getAttrs requires the original case) and that all the names are in the form {skill}-xp, {skill}-lvl, {skill}-gth, etc. You can see that skillXpChanged actually returns a function with a closure of the name of the governing attribute. The first seven on() &nbsp;each call skillXpChanged with a different governing attribute name, triggering on changes to any of the six skills under that attribute's purview. I also added in an eighth on() &nbsp;call for level calculations of the seven governing attributes. I'm also assuming the math is correct for the two Math.round() lines. I just grabbed one of the first JavaScript CDF implementations I could find from a quick Google search, so I make no assertions about its accuracy! =) You could also set up this code to make the various on() &nbsp;calls easier to read (or maybe it's just me?), and with less repetition, something like this: var attributes = { strength: [ 'aggressive-combat', 'clashing-defense', 'athletics', 'breaching', 'hauling', 'initiative' ], constitution: [ 'eroding-combat', 'lasting-defense', 'drinking' 'endurance', 'immunity', 'gait' ], // etc. } attributes = _.mapObject(attributes, v =&gt; 'change:' + v + '-xp'); _.each(attributes, (v, k) =&gt; { on(v.join(' '), skillXpChanged(k + '-lvl')); }); // replaces the first seven calls above on(_.chain(attributes).keys().map(v =&gt; 'change:' + v + '-xp').value().join(' '), function(eventInfo) { // same as previous code block above for change:strength-xp etc. }); If nothing else, putting all the attribute name strings in one place makes it easier to see if you made a typo. ~_^
O.o I'm going to have to study this closely.... also, I have another good approximation that I had found for the mathie bits, which I'm likely going to have to change again anyway unfortunately, seems almost too easy to get stupid good stats, duno, but anyway, I'll look over the code to see just how it works and put it in and all that jibbity jazz. And thank you so much!