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

Using custom status markers to Apply Damage and HPBarTracker scripts.

1778604664

Edited 1778605080
As the title says, I'm trying to use a custom status marker with the above scripts. I found this old thread which got me going in what I think is the right direction, but the thread ended before it got past the problem with parsing the "::" in the icon ID, which is where I think my problem lies. I got the ID of the custom status marker that I want to use ( unconcious [  unconcious::3443139  ] ) from TokenMod: I then added that to the list of status markers in the ApplyDamage script. Now I wanted to edit the HPBarTracker script to do two things: To add the "unconcious" status marker to a token when its bar1 value equals zero (0), add the "dead" status marker (Big Red X) if the value went to less than zero ( <0 ), and remove the "dead" status marker if the bar1 went to >= 0. on('ready', () => { const HPBarNum = 1; const bar = `bar${HPBarNum}_value`; const max = `bar${HPBarNum}_max`; const constrainHPBarToMax = (obj) => { const hpMax = parseInt(obj.get(max),10); if(!isNaN(hpMax) && 'token' === obj.get('subtype') && !obj.get('isdrawing') ){ let hp = parseInt(obj.get(bar),10); let changes = {}; if(hp > hpMax) { hp = hpMax; changes[bar] = hp; changes.status_dead = false; } else if(hp == 0) { hp=0; changes[bar] = hp; changes.status_dead = false; changes.status_unconcious::3443139 = true; } else if(hp < 0) { hp=-1; changes[bar] = hp; changes.status_dead = true; } else { changes.status_dead = false; } obj.set(changes); } }; on("change:token", constrainHPBarToMax); if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){ TokenMod.ObserveTokenChange(constrainHPBarToMax); } if('undefined' !== typeof ApplyDamage && ApplyDamage.registerObserver){ ApplyDamage.registerObserver('change',constrainHPBarToMax); } }); This is the error I get when I restart the sandbox: If I change the name of the status marker in HPBarTracker to just "unconcious" (or anything else) the script works fine - it ignores the bar1 value if it's >= 0 and adds the "dead" marker if the bar1 value is < 0.  I'm not skilled in coding but I can often edit a (simple) existing script to do what I want. Where am I going wrong here?
In your game settings, have you tried viewing the token set to see what the icon's name appears as there? It might be that different names are used for references compared to a few years ago.
If the name is correct, you might want to try `changes['status_unconcious::3443139'] = true;` - the error is likely due to `changes.status_unconcious::3443139 = true;` being invalid JavaScript. Otherwise, for one of my mods, I only ask for the icon name, and the mod resolves the required ID. You can view the code I use here: <a href="https://github.com/steverobertsuk/roll20-api-scripts/blob/sr/condition-tracker-v1-1/ConditionTracker/src/markers.js" rel="nofollow">https://github.com/steverobertsuk/roll20-api-scripts/blob/sr/condition-tracker-v1-1/ConditionTracker/src/markers.js</a>
1778713877
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
This script might be a little more tailored to what you want, I think. Although it works better for me if I change this: &nbsp; if('undefined' !== typeof ApplyDamage &amp;&amp; ApplyDamage.registerObserver){ &nbsp; &nbsp; ApplyDamage.registerObserver('change',onAttributeChange); &nbsp; } to this: if('undefined' !== typeof ApplyDamage &amp;&amp; ApplyDamage.registerObserver){ &nbsp; ApplyDamage.registerObserver('change', accountForHPBarChange); }
Thanks! That did the trick! No being familiar enough with coding (IOW, not really knowing what I'm doing and just winging it) &nbsp; I was trying to simply copy the line `changes.status_dead = false;' and replace the name of the "dead"" marker with `status_unconcious::3443139`.&nbsp; Now I learned something new (Yay!) MidNiteShadow7 said: If the name is correct, you might want to try `changes['status_unconcious::3443139'] = true;` - the error is likely due to `changes.status_unconcious::3443139 = true;` being invalid JavaScript.
Thanks Keith, I've seen that thread while I was looking for clues on how to alter existing scripts. I'm thinking that that script may be overkill for my application since I rarely need to account for temp hp, and since I use health bars visible to all players we don't really need to record the "bloodied" condition. The reason that I want to use the "unconscious" condition is because in my upcoming campaign, the PCs will be learning that in some circumstances they may want to avoid delivering "death blows" to defenseless NPCs because the PC's actions - regardless of tier intentions -&nbsp; are going to matter more than usual. keithcurtis said: This script might be a little more tailored to what you want, I think. Although it works better for me if I change this: &nbsp; if('undefined' !== typeof ApplyDamage &amp;&amp; ApplyDamage.registerObserver){ &nbsp; &nbsp; ApplyDamage.registerObserver('change',onAttributeChange); &nbsp; } to this: if('undefined' !== typeof ApplyDamage &amp;&amp; ApplyDamage.registerObserver){ &nbsp; ApplyDamage.registerObserver('change', accountForHPBarChange); }
On second thought, and because I had absolutely nothing else to do this afternoon, I decided to play with the script that The Aaron posted in that thread. I now saw that his script displays the&nbsp; temporary hit point total as a status marker instead of one of the token bubbles - this made that function much more desirable to me - I'm already using all 4 bubbles and didn't want to give one up. So .... I redefined the TempHPMarker and BloodiesMarker values to use two custom status markers: &nbsp; I kept the variable names the same because the terms bloodied and unconcious are "close enough" for a mnemonic. I then changed the parameters for the "Bloodied" condition from: else if(UseBloodied &amp;&amp; (hp &lt;= (hpMax/2) ) ) To: else if (hp == 0 ) Now I have the display that I want plus as a bonus I get a display of how many Temporary HP the token has! keithcurtis said: This script might be a little more tailored to what you want, I think. Although it works better for me if I change this:
Rick, once you have it all sorted out, can you share your custom script to the community please.&nbsp;
1778869314

Edited 1778869373
Novercalis said: Rick, once you have it all sorted out, can you share your custom script to the community please.&nbsp; Sure, but remember that the original script was created by The Aaron - I just made&nbsp; a few edits as described above. The custom status markers would need to be changed to use either some of the default ones or ones that you own. /* global TokenMod, ChatSetAttr */ on('ready', () =&gt; { // Configuration parameters const HPBarNum = 1; const TempHPMarker = 'aid-2::3443141'; const DeadMarker = 'dead'; const BloodiedMarker = 'unconcious::3443139'; const TempHPAttributeName = 'hp_temp'; const UseBloodied = true; ///////////////////////////////////////////// const clearURL = /images\/4277467\/iQYjFOsYC5JsuOPUCI9RGA\/.*.png/; const bar = `bar${HPBarNum}_value`; const lnk = `bar${HPBarNum}_link`; const max = `bar${HPBarNum}_max`; const unpackSM = (stats) =&gt; stats.split(/,/).reduce((m,v) =&gt; { let p = v.split(/@/); let n = parseInt(p[1] || '0', 10); if(p[0].length) { m[p[0]] = Math.max(n, m[p[0]] || 0); } return m; },{}); const packSM = (o) =&gt; Object.keys(o) .map(k =&gt; ('dead' === k || true === o[k] || o[k]&lt;1 || o[k]&gt;9) ? k : `${k}@${parseInt(o[k])}` ).join(','); const checkTempHP = (obj) =&gt; { let v = parseFloat(obj.get('current')); findObjs({ type: 'graphic', represents: obj.get('characterid') }) .filter( (t) =&gt; t.get(lnk) !== '') .filter( (t) =&gt; !clearURL.test(t.get('imgsrc') ) ) .forEach((g)=&gt;{ let sm = unpackSM(g.get('statusmarkers')); if(v&gt;0){ sm[TempHPMarker]=v; } else { delete sm[TempHPMarker]; } g.set({ statusmarkers: packSM(sm) }); }); }; const assureTempHPMarkers = () =&gt; { let queue = findObjs({ type: 'attribute', name: TempHPAttributeName }); const burndownQueue = ()=&gt;{ if(queue.length){ let attr = queue.shift(); checkTempHP(attr); setTimeout(burndownQueue,0); } }; burndownQueue(); }; const temporalTempHPCache = {}; const accountForHPBarChange = (obj,prev) =&gt; { // 1. did hp change and is it a scale const hpMax = parseInt(obj.get(max),10); let hp = parseInt(obj.get(bar),10); const diff = hp-parseFloat(prev[bar]); if( !isNaN(hpMax) &amp;&amp; diff !== 0 ) { let changes = {}; // 2. does it represent a character // 3. does the hp bar represent an attribute const character = getObj('character',obj.get('represents')); if( diff &lt; 0 &amp;&amp; character &amp;&amp; obj.get(lnk)!=='' ){ // 4. is there temp hp const temp_hp = findObjs({ type: 'attribute', characterid: character.id, name: TempHPAttributeName })[0]; if( temp_hp ) { const now = Date.now(); const tempHP = parseFloat(temp_hp.get('current'))||0; const newTmpHP = Math.max((tempHP+diff),0); const toHeal = tempHP - newTmpHP; const hash=`${character.id}:${hp}:${hpMax}:${toHeal}`; // 5. have we accounted for it. if( !temporalTempHPCache.hasOwnProperty(hash) || (now-temporalTempHPCache[hash].when)&gt;10000) { // calculate necessary change temporalTempHPCache[hash]={ when: now, toHeal: toHeal }; temp_hp.set('current', newTmpHP); checkTempHP(temp_hp); } hp += temporalTempHPCache[hash].toHeal; changes[bar] = hp; } } let sm = unpackSM(obj.get('statusmarkers')); if(hp &gt; hpMax) { hp = hpMax; changes[bar] = hp; delete sm[DeadMarker]; if(UseBloodied) { delete sm[BloodiedMarker]; } } else if(hp &lt; 0) { hp=-1; changes[bar] = hp; sm[DeadMarker] = true; if(UseBloodied) { delete sm[BloodiedMarker]; } } else if (hp == 0 ) { delete sm[DeadMarker]; sm[BloodiedMarker] = true; } else { delete sm[DeadMarker]; if(UseBloodied) { delete sm[BloodiedMarker]; } } changes.statusmarkers = packSM(sm); obj.set(changes); } }; const onAttributeChange = (obj) =&gt; { if(obj.get('name') === TempHPAttributeName){ checkTempHP(obj); } }; on("change:attribute", onAttributeChange); on("change:token", accountForHPBarChange); if('undefined' !== typeof TokenMod &amp;&amp; TokenMod.ObserveTokenChange){ TokenMod.ObserveTokenChange(accountForHPBarChange); } if('undefined' !== typeof ChatSetAttr &amp;&amp; ChatSetAttr.registerObserver){ ChatSetAttr.registerObserver('change',onAttributeChange); } if('undefined' !== typeof ApplyDamage &amp;&amp; ApplyDamage.registerObserver){ ApplyDamage.registerObserver('change', accountForHPBarChange); } assureTempHPMarkers(); });
1778940022

Edited 1778940358
Victor B.
Pro
Sheet Author
API Scripter
You can use libTokenMarkers to get custom status icons.&nbsp; This as written by TheArron.&nbsp; All you need to pass in is the IconName you want.&nbsp; It does the rest.&nbsp;&nbsp; iconTag = libTokenMarkers.getStatus(iconName).getTag()
As I mentioned above, I'm not very familiar with coding (IOW, I don't really know what I'm doing - just winging it)&nbsp; ... :D Where do you use this? Within the script or separately? Victor B. said: You can use libTokenMarkers to get custom status icons.&nbsp; This as written by TheArron.&nbsp; All you need to pass in is the IconName you want.&nbsp; It does the rest.&nbsp;&nbsp; iconTag = libTokenMarkers.getStatus(iconName).getTag()
1778977513
Victor B.
Pro
Sheet Author
API Scripter
libTokenMarkers is a script like many others on roll20.&nbsp; Just find it and add it to your game.&nbsp;&nbsp;
1778980424

Edited 1778980504
Victor B. said: libTokenMarkers is a script like many others on roll20.&nbsp; Just find it and add it to your game.&nbsp;&nbsp; I didn't make myself clear: I already have libTokenMarkers loaded because it's required by other scripts that I use. I was asking where to use the command line that you posted. I currently use a different script to extract status marker definitions, and can also get them from the TokenMod help handout. I'm assuming that the code that you posted would allow me to use the status marker name in my script instead of needing to look up its definition.