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

Custom attribute losing formula when referenced by token

1478964912

Edited 1478964949
Dan
Plus
Sheet Author
Hi Guys,  I'm using Roll20 OGL 5th edition and I play with a custom rule that instead of using regular spells slots, we use the total number of slots as the mana. The level of the spell you cast corresponds to the amount of mana required. So let's say you have a Wizard level 2 that can cast 3 lvl 1 spells, thus it will have 3 mana in total and every level 1 it cast removes 1 from the total mana. Ok that looks the same, however on level 3rd same Wizard gets 2 lvl 2 spells, in other words it adds 2 * 2 to its total mana since each lvl 2 spell slot adds 2 to its total mana. Now the wizard that has 3 lvl 1 and 2 lvl 2 spells has a total of =  3*1 + 2*2 which is 7 slots in total. Casting a lvl 1 removes 1 from the total, same works for casting lvl 2 that removes 2 from the total. Long rest recovers the same way. That said, I created two attributes in the character sheet to represent the total_mana and the current_mana. total_mana is described as: (@{lvl1_slots_total}*1)+(@{lvl2_slots_total}*2)+(@{lvl3_slots_total}*3)+(@{lvl4_slots_total}*4)+(@{lvl5_slots_total}*5)+(@{lvl6_slots_total}*6)+(@{lvl7_slots_total}*7)+(@{lvl8_slots_total}*8)+(@{lvl9_slots_total}*9) current_mana is described as: (@{total_mana})-((@{lvl1_slots_expended}*1)+(@{lvl2_slots_expended}*2)+(@{lvl3_slots_expended}*3)+(@{lvl4_slots_expended}*4)+(@{lvl5_slots_expended}*5)+(@{lvl6_slots_expended}*6)+(@{lvl7_slots_expended}*7)+(@{lvl8_slots_expended}*9)) Finally I create the final attribute that my token will reference that is mana:  @{current_mana} @{total_mana} That works just when I first save, then roll20 updates the mana attribute I created with the actual value (the result of the calculation). The calculation works, but it replaces the formula with the value, and the consequence is that when the player updates the spell slots in the sheet the values don't get recomputed. Either an expended slot or when it levels up and add more total slots. for the example above, right after I reference the attribute from the token when I go back to the sheet attributes I can see mana as: 4 7 Which is the current state of the character sheet, but if you change the character sheet it doesn't update accordingly. Any thoughts?
1478968001

Edited 1478968045
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Hi Daniel, Hmmm, I've made your attributes as you described above and changed the character's level. The attributes stay in the formula form for me unless I link a token's bar to one of these attributes, do you have your token's setup like this as well? There's no way around this without tweaking the sheet. If it didn't work this way, then you would not be able to update attributes via the bars, and I personally think that that trumps this as a needed feature. A workaround I might suggest is that instead of linking a token's bar to current mana, you could make a spellbook macro for each player that would output  ability command buttons to cast their spells to chat. You could organize them by spell level (which would equate to mana) and have the current and total mana displayed at the top of the spellbook. Referencing these attributes in chat will not force them to calculate the way linking them to a token's bar will. Hope that helps, and sorry I couldn't come up with something better, Scott
1478972540

Edited 1478972665
Dan
Plus
Sheet Author
Hi Scott Thanks for the answer! When you level up the amount of spells slots don't update by itself, you have to go there and change manually. The problem happens as you described, when I link them to the token bar. I wouldn't even update via token bar the current mana, because I use the ammunition tracker script that updates spells slots spent automatically, thereafter the formula capturing the same field (expended spell slots) would also make it recalculate automatically. In other words, whenever I cast a spell the mana bar would decrease on its own, but as you said when I link it to the token bar the formula disappears and the attribute holds the resulted value. I think that if roll20 doesn't find the attribute name in the sheet HTML it won't update it automatically, that's why my formula gets overwritten and the value becomes static. I might just make the players do this manually. I would agree that it would be nice for roll20 to not replace formulas inside your attribute with the computed value when you link them to token bars. The technical challenge might be that if the user changes the value through the token, it would persist the value and overwrite the formula in the attribute section. That would make sense for me as a standard behaviour, and an easy way I would suggest to get around it is making an option to have readonly attributes (like a checkbox at the line level), so that when you link attribute to the token it just outputs the computed value without losing the formula and doesn't allow the player to change these values through the token options. That way we could dynamically compute values from the state (like some sort of selector) where the state is the character sheet, this could be a very versatile feature use for many different purposes.
1478972865
The Aaron
Pro
API Scripter
I can't delve into this deeply right now, but I bet a simple API script that watches for changes on those attributes and maintains both value attributes and formula attributes for you could be quite easily written. Probably 20 lines or so. I can look into that later tonight (Russ Hapke is visiting me today!), unless Scott beats me to it. :)
1478972951
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I might suggest that you use the ammunition tracker script to update each individual spell level (since this is what the actual current/total mana is based on) rather than updating current mana directly. This would allow you to have the current/total output to chat when someone casts a spell (and they can therefore see it that way), but won't overwrite the formula.
1478973059
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
The Aaron said: I can't delve into this deeply right now, but I bet a simple API script that watches for changes on those attributes and maintains both value attributes and formula attributes for you could be quite easily written. Probably 20 lines or so. I can look into that later tonight (Russ Hapke is visiting me today!), unless Scott beats me to it. :) Heh, I've got enough on my plate finishing up my Jukebox master control script (thought it was going to be soooo simple when I started it, poor naive me), I'll leave this to you if you want to tackle it.
1479073950
The Aaron
Pro
API Scripter
Ok, I've got something for you to try.   CAVEAT: this will overwrite your existing attributes! You may wish to try it out on a copy of your game.  If you create any of the three attributes [ total_mana, current_mana, mana ], the script will create the others and fill all 3 with pertinent information the next time a spell is cast or a long rest is taken.  It will also keep them up to date with the right values at any of those events and on restart of the API. Here's the code: on('ready',function(){     "use strict";     var     getManaTotalByCharacterID = function(charid){         return 0 +             (getAttrByName(charid,'lvl1_slots_total')*1)+             (getAttrByName(charid,'lvl2_slots_total')*2)+             (getAttrByName(charid,'lvl3_slots_total')*3)+             (getAttrByName(charid,'lvl4_slots_total')*4)+             (getAttrByName(charid,'lvl5_slots_total')*5)+             (getAttrByName(charid,'lvl6_slots_total')*6)+             (getAttrByName(charid,'lvl7_slots_total')*7)+             (getAttrByName(charid,'lvl8_slots_total')*8)+             (getAttrByName(charid,'lvl9_slots_total')*9);     },     getManaUsedByCharacterID = function(charid){         return 0 +             (getAttrByName(charid,'lvl1_slots_expended')*1)+             (getAttrByName(charid,'lvl2_slots_expended')*2)+             (getAttrByName(charid,'lvl3_slots_expended')*3)+             (getAttrByName(charid,'lvl4_slots_expended')*4)+             (getAttrByName(charid,'lvl5_slots_expended')*5)+             (getAttrByName(charid,'lvl6_slots_expended')*6)+             (getAttrByName(charid,'lvl7_slots_expended')*7)+             (getAttrByName(charid,'lvl8_slots_expended')*8)+             (getAttrByName(charid,'lvl9_slots_expended')*9);     },     assureAttrs = function (charid, attrs){         let usedMana=getManaUsedByCharacterID(charid),             totalMana=getManaTotalByCharacterID(charid);         (attrs.total_mana||createObj('attribute',{                 characterid: charid,                 name: 'total_mana'             })).set('current',totalMana);         (attrs.current_mana||createObj('attribute',{                 characterid: charid,                 name: 'current_mana'             })).set('current',totalMana-usedMana);         (attrs.mana||createObj('attribute',{                 characterid: charid,                 name: 'mana'             })).set({                 current: totalMana-usedMana,                 max:totalMana             });     },     verifyAttributes = function(){         _.chain(filterObjs((o)=> 'attribute'===o.get('type') && _.contains(['mana','total_mana','current_mana'],o.get('name'))))             .reduce((m,o)=>{                 let cid=o.get('characterid');                 m[cid] = (m[cid]||{});                 m[cid][o.get('name')]=o;                 return m;             },{})             .each((v,k)=>{                 assureAttrs(k,v);             })             ;     },     detectCasting = function(msg){         if('api'===msg.type && !msg.rolltemplate){             if(msg.content.match(/^!(longrest|spelltracking)/)){                 _.defer(verifyAttributes);             }         } else if(msg.playerid.toLowerCase() !== 'api' && msg.rolltemplate && msg.content.match(/{{\s*spelllevel\s*=\s*\d+\s*}}/) ) {             _.defer(verifyAttributes);         }     },          handleAttrChanges = function(obj,prev){         if(_.contains([             'lvl1_slots_total', 'lvl2_slots_total', 'lvl3_slots_total',             'lvl4_slots_total', 'lvl5_slots_total', 'lvl6_slots_total',             'lvl7_slots_total', 'lvl8_slots_total', 'lvl9_slots_total',             'lvl1_slots_expended', 'lvl2_slots_expended', 'lvl3_slots_expended',             'lvl4_slots_expended', 'lvl5_slots_expended', 'lvl6_slots_expended',             'lvl7_slots_expended', 'lvl8_slots_expended', 'lvl9_slots_expended',             'total_mana', 'current_mana', 'mana' ], prev.name)){                          let charid = obj.get('characterid'),                 a=_.reduce(filterObjs((o) => {                     return o.get('characterid')=== charid &&                      'attribute'===o.get('type') &&                      _.contains(['mana','total_mana','current_mana'], o.get('name'));             }),(m,o)=> {                 m[o.get('name')]=o;                 return m;             }, {});             if(_.keys(a).length){                 assureAttrs(charid,a);             }         }     };     verifyAttributes();     on('change:attribute', handleAttrChanges);     on('chat:message', detectCasting); });
1479133155
Dan
Plus
Sheet Author
Ohh thanks for helping out Aaron I will try thanks a lot!
1479148059
The Aaron
Pro
API Scripter
Cool.  Let me know if that works and there are any changes to make.  I could probably make some parts of it more optimal (spells and long rest cause everyone to update, rather than just the person casting or the person resting), but it seemed pretty fast on my limited test. I could also make it just create all the attributes whenever one of those slots changed (or even if they have a positive value). Anyway, I hope it works well for you! Happy Rolling!
1479572808
Dan
Plus
Sheet Author
I added the script and it is working perfectly fine! Thanks for helping out There is just one small problem using the ammunition tracker (Companion OGL) script for tracking spells automatically. It seems that when the Companion script updates the lvlX_slots_expended attribute the Mana script doesn't get notified, as if the Companion wasn't broadcasting some sort of event update. Do you have any ideas?
1479617227
The Aaron
Pro
API Scripter
The API changing an attribute doesn't cause an event so this script tries to infer the change. If it ever gets out of sync, it should get righted the next time someone casts a spell. You can also run !longrest with no arguments to force it to recalculate for everyone. If that's not working right, let me know and I'll try to figure out the issue.