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

Blood loss, bleeding, and stabilization

I recently added blood&amp;honor API to my game. I like it except that you can't really turn it off without adding terms (like noblood) to the token gmnotes or recovering hit points. So bandages or other stabilization effects won't stop bleeding. I was able to open the code to the API to make the point of blood splatters and pools occur after greater hit point loss and to reduce the size of the splatter and pools, but I want to be able to add stabilized or bandaged (either one would work) to the gmnotes and have it take effect, stopping the bloodloss and bloodtrails. So I found The Aaron's set-gmnote API but cannot get it to work (it crashes the API sandbox because N is undefined. I want to be able to tag cure light wounds, first aid, and other healing effects with a !set-gmnote stabilized and have stabilized be a factor in blood and honor to not have the bleed effect (similar to noblood). However, I want stabilize to vanish if/when the token takes more damage.&nbsp; Here is the blood and honor script: ///////////////////////////////////////////////// /***********************************************/ var BloodAndHonor = { author: { name: "John C." || "Echo" || "SplenectomY", company: "Team Asshat" || "The Alehounds", contact: "<a href="mailto:echo@TeamAsshat.com" rel="nofollow">echo@TeamAsshat.com</a>", }, version: "0.8", gist: "<a href="https://gist.github.com/SplenectomY/097dac3e427ec50f32c9" rel="nofollow">https://gist.github.com/SplenectomY/097dac3e427ec50f32c9</a>", forum: "<a href="https://app.roll20.net/forum/post/1477230/" rel="nofollow">https://app.roll20.net/forum/post/1477230/</a>", wiki: "<a href="https://wiki.roll20.net/Script:Blood_And_Honor:_Automatic_blood_spatter,_pooling_and_trail_effects" rel="nofollow">https://wiki.roll20.net/Script:Blood_And_Honor:_Automatic_blood_spatter,_pooling_and_trail_effects</a>", /***********************************************/ ///////////////////////////////////////////////// // This value should match the size of a standard grid in your campaign // Default is 70 px x 70 px square, Roll20's default. tokenSize: 70, // If you have it installed, this will plug in TheAaron's isGM auth module, // which will make it so only the GM can use the !clearblood command // Change to "true" if you want to check for authorization useIsGM: false, // YOU MUST ADD YOUR OWN SPATTERS AND POOLS TO YOUR LIBRARY // AND GET THE IMAGE LINK VIA YOUR WEB BROWSER. // FOLLOW THE INSTRUCTIONS HERE: //&nbsp; <a href="https://wiki.roll20.net/API:Objects#imgsrc_and_av" rel="nofollow">https://wiki.roll20.net/API:Objects#imgsrc_and_av</a>... // You can add as many as you'd like to either category. // Spatters are also used for blood trails. spatters: [ "<a href="https://s3.amazonaws.com/files.d20.io/images/7029416/E1Ltkjvnl2vRpTy8tEzizw/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029416/E1Ltkjvnl2vRpTy8tEzizw/thumb.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/thumb.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/thumb.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/thumb.png?1420637808" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/thumb.png?1420637808</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/thumb.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/thumb.png?1420637339</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/thumb.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/thumb.png?1420637339</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/thumb.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/thumb.png?1420637342</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/thumb.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/thumb.png?1420637342</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/thumb.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/thumb.png?1420637343</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/thumb.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/thumb.png?1420637343</a>", ], pools: [ "<a href="https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/thumb.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/thumb.png?1420637356</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/thumb.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/thumb.png?1420637356</a>", ], chooseBlood: function chooseBlood(type) { if (type == "spatter") return BloodAndHonor.spatters[randomInteger(BloodAndHonor.spatters.length) - 1]; if (type == "pool") return BloodAndHonor.pools[randomInteger(BloodAndHonor.pools.length) - 1]; }, getOffset: function getOffset() { if (randomInteger(2) == 1) return 1; else return -1; }, bloodColor: function bloodColor(gmnotes) { if (gmnotes.indexOf("bloodcolor_purple") !== -1) return "#0000ff"; if (gmnotes.indexOf("bloodcolor_blue") !== -1) return "#00ffff"; if (gmnotes.indexOf("bloodcolor_orange") !== -1) return "#ffff00"; if (gmnotes.indexOf("bloodcolor_green") !== -1) return "#00ff4c"; if (gmnotes.indexOf("bloodcolor_white") !== -1) return "#ffffff"; else return "transparent" }, createBlood: function createBlood(gPage_id,gLeft,gTop,gWidth,gType,gColor) { gLeft = gLeft + (randomInteger(Math.floor(gWidth / 2)) * BloodAndHonor.getOffset()); gTop = gTop + (randomInteger(Math.floor(gWidth / 2)) * BloodAndHonor.getOffset()); setTimeout(function(){ toFront(fixedCreateObj("graphic",{ imgsrc: gType, gmnotes: "blood", pageid: gPage_id, left: gLeft, tint_color: gColor, top: gTop, rotation: randomInteger(360) - 1, width: gWidth, height: gWidth, layer: "map", })); },50); }, timeout: 0, onTimeout: function theFinalCountdown() { if (BloodAndHonor.timeout &gt; 0) { BloodAndHonor.timeout--; } else { return; } } }; fixedCreateObj = (function () { return function () { var obj = createObj.apply(this, arguments); if (obj &amp;&amp; !obj.fbpath) { obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/"); } return obj; }; }()); on("ready", function(obj) { setInterval(function(){BloodAndHonor.onTimeout()},1000); on("change:graphic:bar2_value", function(obj, prev) { if (obj.get("bar2_max") === "" || obj.get("layer") != "objects" || (obj.get("gmnotes")).indexOf("noblood") !== -1) return; // Create spatter near token if "bloodied". // Chance of spatter depends on severity of damage else if (obj.get("bar2_value") &lt;= obj.get("bar2_max") / 3 &amp;&amp; prev["bar2_value"] &gt; obj.get("bar2_value") &amp;&amp; obj.get("bar2_value") &gt; 0) { if (randomInteger(obj.get("bar2_max")) &gt; obj.get("bar2_value")) { var bloodMult = 0 + ((obj.get("bar2_value") - prev["bar2_value"]) / obj.get("bar2_max")); BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize * bloodMult), BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); } } // Create pool near token if health drops below 1. else if (obj.get("bar2_value") &lt;= 0) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize * 1), BloodAndHonor.chooseBlood("pool"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); } }); //Make blood trails, chance goes up depending on how injured a token is on("change:graphic:lastmove", function(obj) { if (BloodAndHonor.timeout == 0) { if (obj.get("bar2_value") &lt;= obj.get("bar2_max") / 3 &amp;&amp; (obj.get("gmnotes")).indexOf("noblood") == -1) { if (randomInteger(obj.get("bar2_max")) &gt; obj.get("bar2_value")) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize / 3), BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); BloodAndHonor.timeout += 2; } } } }); on("chat:message", function(msg) {n if (msg.type == "api" &amp;&amp; msg.content.indexOf("!clearblood") !== -1) { if (BloodAndHonor.useIsGM &amp;&amp; !isGM(msg.playerid)) { sendChat(msg.who,"/w " + msg.who + " You are not authorized to use that command!"); return; } else { objects = filterObjs(function(obj) { if(obj.get("type") == "graphic" &amp;&amp; obj.get("gmnotes") == "blood") return true; else return false; }); _.each(objects, function(obj) { obj.set("left",0); obj.set("top",0); }); } } }); }); Here is The Aaron's set-gmnotes script: on('ready',function(){ &nbsp; &nbsp; 'use strict'; &nbsp; &nbsp; const cmdregex=/^(!(?:set|add)-(?:c?gmnote|bio))\s*/, &nbsp; &nbsp; &nbsp; &nbsp; formatMsg = (c)=&gt;c.replace(cmdregex,'').replace(/(^{{\s*|\s*}}$)/gm,''); &nbsp; &nbsp; on('chat:message',function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; if('api' === msg.type) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let match=msg.content.match(cmdregex); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(match &amp;&amp; playerIsGM(msg.playerid) ){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let content = formatMsg(msg.content), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; who=(getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(_.has(msg,'inlinerolls')){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; content = _.chain(msg.inlinerolls) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var ti=_.reduce(v.results.rolls,function(m2,v2){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(_.has(v2,'table')){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m2.push(_.reduce(v2.results,function(m3,v3){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m3.push(v3.tableItem.name); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m3; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },[]).join(', ')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m2; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },[]).join(', '); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m['$[['+k+']]']= (ti.length &amp;&amp; ti) || v.results.total || 0; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },{}) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m.replace(k,v); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },content) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .value(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(content.length){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let tokens = _.chain(msg.selected) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map( s =&gt; getObj('graphic',s._id)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch(match[1]){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-gmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-cgmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-bio': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bio: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-gmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.each( o =&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: `${o.get('gmnotes')}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-cgmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.get('gmnotes',(notes)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.defer(()=&gt;o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: `${notes}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-bio': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.get('bio',(notes)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.defer(()=&gt;o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bio: `${notes}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat('',`/w "${who}" &lt;div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;Use &lt;code&gt;COMMNAD Some Text&lt;/code&gt; or &lt;code&gt;COMMAND {{ some multi-line text}}&lt;/code&gt;.&lt;/div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;&lt;b&gt;Commands:&lt;/b&gt;&lt;/div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;&lt;ul&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-gmnote&lt;/code&gt; -- Replaces contents of token gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-cgmnote&lt;/code&gt; -- Replaces contents of character gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-bio&lt;/code&gt; -- Replaces contents of character bio.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-gmnote&lt;/code&gt; -- Appends to contents of token gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-cgmnote&lt;/code&gt; -- Appends to contents of character gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-bio&lt;/code&gt; -- Appends to contents of character bio.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;/ul&gt;&lt;/div&gt;` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); Lastly, I would like to have a bleed effect that can be added through gmnotes that has a token lose 1 hit point on thier activation. This could go into effect when "bleed" is added to their gmnotes or some other means. I could do it with powercards or scriptcards tucked away in onmyturn ability, but I would have to add it to every character. So if a universal API could do that, it would be awesome.
1625750808

Edited 1625769914
timmaugh
Pro
API Scripter
Try this... the code is untested, but these were fairly minor changes. This implements your desire to have a "stabilized" note trigger certain behaviors. It does NOT implement the "bleed" request you had at the end of your post. If the token takes damage (represented in bar2), then it will remove the "stabilized" term from the gmnotes If the term "stabilized" appears in the gmnotes of the token, it will not generate new blood effects 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 ///////////////////////////////////////////////// /***********************************************/ var BloodAndHonor = { author: { name: "John C." || "Echo" || "SplenectomY" , company: "Team Asshat" || "The Alehounds" , contact: "<a href="mailto:echo@TeamAsshat.com" rel="nofollow">echo@TeamAsshat.com</a>" , }, version: "0.8" , gist: "<a href="https://gist.github.com/SplenectomY/097dac3e427ec50f32c9" rel="nofollow">https://gist.github.com/SplenectomY/097dac3e427ec50f32c9</a>" , forum: "<a href="https://app.roll20.net/forum/post/1477230/" rel="nofollow">https://app.roll20.net/forum/post/1477230/</a>" , wiki: "<a href="https://wiki.roll20.net/Script:Blood_And_Honor:_Automatic_blood_spatter,_pooling_and_trail_effects" rel="nofollow">https://wiki.roll20.net/Script:Blood_And_Honor:_Automatic_blood_spatter,_pooling_and_trail_effects</a>" , /***********************************************/ ///////////////////////////////////////////////// // This value should match the size of a standard grid in your campaign // Default is 70 px x 70 px square, Roll20's default. tokenSize: 70 , // If you have it installed, this will plug in TheAaron's isGM auth module, // which will make it so only the GM can use the !clearblood command // Change to "true" if you want to check for authorization useIsGM: false , // YOU MUST ADD YOUR OWN SPATTERS AND POOLS TO YOUR LIBRARY // AND GET THE IMAGE LINK VIA YOUR WEB BROWSER. // FOLLOW THE INSTRUCTIONS HERE: // <a href="https://wiki.roll20.net/API:Objects#imgsrc_and_av" rel="nofollow">https://wiki.roll20.net/API:Objects#imgsrc_and_av</a>... // You can add as many as you'd like to either category. // Spatters are also used for blood trails. spatters: [ "<a href="https://s3.amazonaws.com/files.d20.io/images/7029416/E1Ltkjvnl2vRpTy8tEzizw/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029416/E1Ltkjvnl2vRpTy8tEzizw/thumb.png?1420637338</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/thumb.png?1420637338</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/thumb.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/thumb.png?1420637338</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/thumb.png?1420637808" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/thumb.png?1420637808</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/thumb.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/thumb.png?1420637339</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/thumb.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/thumb.png?1420637339</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/thumb.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/thumb.png?1420637342</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/thumb.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/thumb.png?1420637342</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/thumb.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/thumb.png?1420637343</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/thumb.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/thumb.png?1420637343</a>" , ], pools: [ "<a href="https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/thumb.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/thumb.png?1420637356</a>" , "<a href="https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/thumb.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/thumb.png?1420637356</a>" , ], chooseBlood: function chooseBlood(type) { if (type === "spatter" ) return BloodAndHonor.spatters[randomInteger(BloodAndHonor.spatters.length) - 1 ]; if (type === "pool" ) return BloodAndHonor.pools[randomInteger(BloodAndHonor.pools.length) - 1 ]; }, getOffset: function getOffset() { if (randomInteger( 2 ) === 1 ) return 1 ; else return - 1 ; }, bloodColor: function bloodColor(gmnotes) { if (gmnotes.indexOf( "bloodcolor_purple" ) !== - 1 ) return "#0000ff" ; if (gmnotes.indexOf( "bloodcolor_blue" ) !== - 1 ) return "#00ffff" ; if (gmnotes.indexOf( "bloodcolor_orange" ) !== - 1 ) return "#ffff00" ; if (gmnotes.indexOf( "bloodcolor_green" ) !== - 1 ) return "#00ff4c" ; if (gmnotes.indexOf( "bloodcolor_white" ) !== - 1 ) return "#ffffff" ; else return "transparent" }, createBlood: function createBlood(gPage_id, gLeft, gTop, gWidth, gType, gColor) { gLeft = gLeft + (randomInteger( Math .floor(gWidth / 2 )) * BloodAndHonor.getOffset()); gTop = gTop + (randomInteger( Math .floor(gWidth / 2 )) * BloodAndHonor.getOffset()); setTimeout( function () { toFront(fixedCreateObj( "graphic" , { imgsrc: gType, gmnotes: "blood" , pageid: gPage_id, left: gLeft, tint_color: gColor, top: gTop, rotation: randomInteger( 360 ) - 1 , width: gWidth, height: gWidth, layer: "map" , })); }, 50 ); }, timeout: 0 , onTimeout: function theFinalCountdown() { if (BloodAndHonor.timeout &gt; 0 ) { BloodAndHonor.timeout--; } else { return ; } } }; fixedCreateObj = ( function () { return function () { var obj = createObj.apply( this , arguments); if (obj &amp;&amp; !obj.fbpath) { obj.fbpath = obj.changed._fbpath.replace( /([^\/]*\/){4}/ , "/" ); } return obj; }; }()); on( "ready" , function (obj) { setInterval( function () { BloodAndHonor.onTimeout() }, 1000 ); on( "change:graphic:bar2_value" , function (obj, prev) { if (obj.get( "bar2_max" ) === "" || obj.get( "layer" ) !== "objects" || (obj.get( "gmnotes" )).indexOf( "noblood" ) !== - 1 ) return ; // Create spatter near token if "bloodied". // Chance of spatter depends on severity of damage if (prev[ "bar2_value" ] &gt; obj.get( "bar2_value" )) { obj.set({ gmnotes: obj.get( 'gmnotes' ).replace( /stabilized/gim , '' ) }); if (obj.get( "bar2_value" ) &gt; 0 ) { if (obj.get( "bar2_value" ) &lt;= obj.get( "bar2_max" ) / 3 &amp;&amp; randomInteger(obj.get( "bar2_max" )) &gt; obj.get( "bar2_value" )) { var bloodMult = 0 + ((obj.get( "bar2_value" ) - prev[ "bar2_value" ]) / obj.get( "bar2_max" )); BloodAndHonor.createBlood(obj.get( "_pageid" ), obj.get( "left" ), obj.get( "top" ), Math .floor(BloodAndHonor.tokenSize * bloodMult), BloodAndHonor.chooseBlood( "spatter" ), BloodAndHonor.bloodColor(obj.get( "gmnotes" ))); } } // Create pool near token if health drops below 1. else if (obj.get( "bar2_value" ) &lt;= 0 ) { BloodAndHonor.createBlood(obj.get( "_pageid" ), obj.get( "left" ), obj.get( "top" ), Math .floor(BloodAndHonor.tokenSize * 1 ), BloodAndHonor.chooseBlood( "pool" ), BloodAndHonor.bloodColor(obj.get( "gmnotes" ))); } } }); //Make blood trails, chance goes up depending on how injured a token is on( "change:graphic:lastmove" , function (obj) { if (BloodAndHonor.timeout === 0 ) { if (obj.get( "bar2_value" ) &lt;= obj.get( "bar2_max" ) / 3 &amp;&amp; (obj.get( "gmnotes" )).indexOf( "noblood" ) === - 1 &amp;&amp; obj.get( "gmnotes" ).indexOf( "stabilized" ) === - 1 ) { if (randomInteger(obj.get( "bar2_max" )) &gt; obj.get( "bar2_value" )) { BloodAndHonor.createBlood(obj.get( "_pageid" ), obj.get( "left" ), obj.get( "top" ), Math .floor(BloodAndHonor.tokenSize / 3 ), BloodAndHonor.chooseBlood( "spatter" ), BloodAndHonor.bloodColor(obj.get( "gmnotes" ))); BloodAndHonor.timeout += 2 ; } } } }); on( "chat:message" , function (msg) { if (msg.type === "api" &amp;&amp; msg.content.indexOf( "!clearblood" ) !== - 1 ) { if (BloodAndHonor.useIsGM &amp;&amp; !isGM(msg.playerid)) { sendChat(msg.who, "/w " + msg.who + " You are not authorized to use that command!" ); return ; } else { objects = filterObjs( function (obj) { if (obj.get( "type" ) === "graphic" &amp;&amp; obj.get( "gmnotes" ) === "blood" ) return true ; else return false ; }); _.each(objects, function (obj) { obj.set( "left" , 0 ); obj.set( "top" , 0 ); }); } } }); });
Awesome. much appreciated. Now I just need to figure out how to get setgmnote to work lol.
I currently can't get the setgmnotes api to work because it returns with a n is not defined (or undefined) error.
1625764133
The Aaron
Roll20 Production Team
API Scripter
Can you post the code you're using, the command you're issuing, and the exact error it's returning (with callstack and such)?
1625764345
The Aaron
Roll20 Production Team
API Scripter
This is the version of the code that I have: on('ready',function(){ 'use strict'; const cmdregex=/^(!(?:set|add)-(?:c?gmnote|bio))\s*/, formatMsg = (c)=&gt;c.replace(cmdregex,'').replace(/(^{{\s*|\s*}}$)/gm,''); on('chat:message',function(msg){ if('api' === msg.type) { let match=msg.content.match(cmdregex); if(match &amp;&amp; playerIsGM(msg.playerid) ){ let content = formatMsg(msg.content), who=(getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); if(_.has(msg,'inlinerolls')){ content = _.chain(msg.inlinerolls) .reduce(function(m,v,k){ var ti=_.reduce(v.results.rolls,function(m2,v2){ if(_.has(v2,'table')){ m2.push(_.reduce(v2.results,function(m3,v3){ m3.push(v3.tableItem.name); return m3; },[]).join(', ')); } return m2; },[]).join(', '); m['$[['+k+']]']= (ti.length &amp;&amp; ti) || v.results.total || 0; return m; },{}) .reduce(function(m,v,k){ return m.replace(k,v); },content) .value(); } if(content.length){ let tokens = _.chain(msg.selected) .map( s =&gt; getObj('graphic',s._id)) .reject(_.isUndefined) ; switch(match[1]){ case '!set-gmnote': tokens.each( o =&gt; { o.set({ gmnotes: content }); }); break; case '!set-cgmnote': tokens.map( o =&gt; getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o =&gt; { o.set({ gmnotes: content }); }); break; case '!set-bio': tokens.map( o =&gt; getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o =&gt; { o.set({ bio: content }); }); break; case '!add-gmnote': tokens.each( o =&gt;{ o.set({ gmnotes: `${o.get('gmnotes')}&lt;br&gt;\n${content}` }); }); break; case '!add-cgmnote': tokens.map( o =&gt; getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o =&gt; { o.get('gmnotes',(notes)=&gt;{ _.defer(()=&gt;o.set({ gmnotes: `${notes}&lt;br&gt;\n${content}` })); }); }); break; case '!add-bio': tokens.map( o =&gt; getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o =&gt; { o.get('bio',(notes)=&gt;{ _.defer(()=&gt;o.set({ bio: `${notes}&lt;br&gt;\n${content}` })); }); }); break; } } else { sendChat('',`/w "${who}" &lt;div&gt;`+ `&lt;div&gt;Use &lt;code&gt;COMMNAD Some Text&lt;/code&gt; or &lt;code&gt;COMMAND {{ some multi-line text}}&lt;/code&gt;.&lt;/div&gt;`+ `&lt;div&gt;&lt;b&gt;Commands:&lt;/b&gt;&lt;/div&gt;`+ `&lt;div&gt;&lt;ul&gt;`+ `&lt;li&gt;&lt;code&gt;!set-gmnote&lt;/code&gt; -- Replaces contents of token gmnotes.&lt;/li&gt;`+ `&lt;li&gt;&lt;code&gt;!set-cgmnote&lt;/code&gt; -- Replaces contents of character gmnotes.&lt;/li&gt;`+ `&lt;li&gt;&lt;code&gt;!set-bio&lt;/code&gt; -- Replaces contents of character bio.&lt;/li&gt;`+ `&lt;li&gt;&lt;code&gt;!add-gmnote&lt;/code&gt; -- Appends to contents of token gmnotes.&lt;/li&gt;`+ `&lt;li&gt;&lt;code&gt;!add-cgmnote&lt;/code&gt; -- Appends to contents of character gmnotes.&lt;/li&gt;`+ `&lt;li&gt;&lt;code&gt;!add-bio&lt;/code&gt; -- Appends to contents of character bio.&lt;/li&gt;`+ `&lt;/ul&gt;&lt;/div&gt;` ); } } } }); }); I just ran: !set-gmnote stabilized And it worked for me.
The Code: on('ready',function(){ &nbsp; &nbsp; 'use strict'; &nbsp; &nbsp; const cmdregex=/^(!(?:set|add)-(?:c?gmnote|bio))\s*/, &nbsp; &nbsp; &nbsp; &nbsp; formatMsg = (c)=&gt;c.replace(cmdregex,'').replace(/(^{{\s*|\s*}}$)/gm,''); &nbsp; &nbsp; on('chat:message',function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; if('api' === msg.type) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let match=msg.content.match(cmdregex); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(match &amp;&amp; playerIsGM(msg.playerid) ){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let content = formatMsg(msg.content), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; who=(getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(_.has(msg,'inlinerolls')){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; content = _.chain(msg.inlinerolls) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var ti=_.reduce(v.results.rolls,function(m2,v2){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(_.has(v2,'table')){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m2.push(_.reduce(v2.results,function(m3,v3){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m3.push(v3.tableItem.name); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m3; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },[]).join(', ')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m2; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },[]).join(', '); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m['$[['+k+']]']= (ti.length &amp;&amp; ti) || v.results.total || 0; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },{}) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m.replace(k,v); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },content) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .value(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(content.length){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let tokens = _.chain(msg.selected) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map( s =&gt; getObj('graphic',s._id)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch(match[1]){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-gmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-cgmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!set-bio': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bio: content &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-gmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.each( o =&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: `${o.get('gmnotes')}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-cgmnote': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.get('gmnotes',(notes)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.defer(()=&gt;o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gmnotes: `${notes}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!add-bio': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tokens.map( o =&gt; getObj('character',o.get('represents'))) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reject(_.isUndefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each( o =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.get('bio',(notes)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.defer(()=&gt;o.set({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bio: `${notes}&lt;br&gt;\n${content}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat('',`/w "${who}" &lt;div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;Use &lt;code&gt;COMMNAD Some Text&lt;/code&gt; or &lt;code&gt;COMMAND {{ some multi-line text}}&lt;/code&gt;.&lt;/div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;&lt;b&gt;Commands:&lt;/b&gt;&lt;/div&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;div&gt;&lt;ul&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-gmnote&lt;/code&gt; -- Replaces contents of token gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-cgmnote&lt;/code&gt; -- Replaces contents of character gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!set-bio&lt;/code&gt; -- Replaces contents of character bio.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-gmnote&lt;/code&gt; -- Appends to contents of token gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-cgmnote&lt;/code&gt; -- Appends to contents of character gmnotes.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;li&gt;&lt;code&gt;!add-bio&lt;/code&gt; -- Appends to contents of character bio.&lt;/li&gt;`+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `&lt;/ul&gt;&lt;/div&gt;` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); The Command: !set-gmnote --stabilize The Error: Your scripts are currently disabled due to an error that was detected. Please make appropriate changes to your script's code and click the "Save Script" button. We will then attempt to start running the scripts again.&nbsp; More info... &nbsp;If this script was installed from the Script Library, you might find help in the Community API Forum. For reference, the error message generated was:&nbsp; ReferenceError: n is not defined ReferenceError: n is not defined at apiscript.js:11677:3 at eval (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:161:1), &lt;anonymous&gt;:65:16) at Object.publish (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:161:1), &lt;anonymous&gt;:70:8) at /home/node/d20-api-server/api.js:1721:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Zd.Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:111:400
1625764654
timmaugh
Pro
API Scripter
It works for me... the sandbox not only boots up, it will process a direct command: !add-gmnote Stabilized ...and it will process a command generated by another script. Even though I am not getting the error you are getting, I did have to configure SelectManager to re-populate the PlayerID for the message. GMNotes looks for a playerID, and on a script-generated message, the player is "API". To restore the playerID to the GMNotes message in this case, you have to instruct SM to give it back: !smconfig +playerid Note: This is a global setting. For the most part, this might be fine. However there are certain campaign-listener scripts that issue commands, and other scripts that receive those commands and do different things with them based on whether the playerID is filled with an actual player's ID or with "API". In those cases, you might need to turn this SM setting on, run your command, then turn it off again. After that, try this version of the GMNotes script (exactly the same except that it adds the code to track line offsets for error messages). If you still get the "n is undefined" error after making the above SM Config tweak, then please post either the A) derived line number (the line number Roll20 provides for the error minus the offset reported in your script console), or the line number you are provided and the offset. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 // starting with a remark var API_Meta = API_Meta || {}; API_Meta.GMNotes = { offset: Number .MAX_SAFE_INTEGER, lineCount: - 1 }; { try { throw new Error ( '' ); } catch (e) { API_Meta.GMNotes.offset = ( parseInt (e.stack.split( /\n/ )[ 1 ].replace( /^.*:(\d+):.*$/ , '$1' ), 10 ) - ( 5 )); } } on( 'ready' , function (){ 'use strict' ; log( ` GM NOTES offset is: ${API_Meta.GMNotes.offset} ` ); const cmdregex= /^(!(?:set|add)-(?:c?gmnote|bio))\s*/ , formatMsg = (c)=&gt;c.replace(cmdregex, '' ).replace( /(^{{\s*|\s*}}$)/gm , '' ); on( 'chat:message' , function (msg){ if ( 'api' === msg.type) { let match=msg.content.match(cmdregex); if (match &amp;&amp; playerIsGM(msg.playerid) ){ let content = formatMsg(msg.content), who=(getObj( 'player' ,msg.playerid)||{get:()=&gt; 'API' }).get( '_displayname' ); if (_.has(msg, 'inlinerolls' )){ content = _.chain(msg.inlinerolls) .reduce( function (m,v,k){ var ti=_.reduce(v.results.rolls, function (m2,v2){ if (_.has(v2, 'table' )){ m2.push(_.reduce(v2.results, function (m3,v3){ m3.push(v3.tableItem.name); return m3; },[]).join( ', ' )); } return m2; },[]).join( ', ' ); m[ '$[[' +k+ ']]' ]= (ti.length &amp;&amp; ti) || v.results.total || 0 ; return m; },{}) .reduce( function (m,v,k){ return m.replace(k,v); },content) .value(); } if (content.length){ let tokens = _.chain(msg.selected) .map( s =&gt; getObj( 'graphic' ,s._id)) .reject(_.isUndefined); switch (match[ 1 ]){ case '!set-gmnote' : tokens.each( o =&gt; { o.set({ gmnotes: content }); }); break ; case '!set-cgmnote' : tokens.map( o =&gt; getObj( 'character' ,o.get( 'represents' ))) .reject(_.isUndefined) .each( o =&gt; { o.set({ gmnotes: content }); }); break ; case '!set-bio' : tokens.map( o =&gt; getObj( 'character' ,o.get( 'represents' ))) .reject(_.isUndefined) .each( o =&gt; { o.set({ bio: content }); }); break ; case '!add-gmnote' : tokens.each( o =&gt;{ o.set({ gmnotes: ` ${o.get( 'gmnotes' )}&lt;br&gt; \ n${content} ` }); }); break ; case '!add-cgmnote' : tokens.map( o =&gt; getObj( 'character' ,o.get( 'represents' ))) .reject(_.isUndefined) .each( o =&gt; { o.get( 'gmnotes' ,(notes)=&gt;{ _.defer(()=&gt;o.set({ gmnotes: ` ${notes}&lt;br&gt; \ n${content} ` })); }); }); break ; case '!add-bio' : tokens.map( o =&gt; getObj( 'character' ,o.get( 'represents' ))) .reject(_.isUndefined) .each( o =&gt; { o.get( 'bio' ,(notes)=&gt;{ _.defer(()=&gt;o.set({ bio: ` ${notes}&lt;br&gt; \ n${content} ` })); }); }); break ; } } else { sendChat( '' , ` /w "${who}" &lt;div&gt; ` + ` &lt;div&gt;Use &lt;code&gt;COMMNAD Some Text&lt; /code&gt; or &lt;code&gt;COMMAND {{ some multi-line text}}&lt;/code&gt;.&lt;/div&gt;`+ ` &lt;div&gt;&lt;b&gt;Commands:&lt; /b&gt;&lt;/div&gt;`+ ` &lt;div&gt;&lt;ul&gt; ` + ` &lt;li&gt;&lt;code&gt;!set-gmnote&lt; /code&gt; -- Replaces contents of token gmnotes.&lt;/li&gt;`+ ` &lt;li&gt;&lt;code&gt;!set-cgmnote&lt; /code&gt; -- Replaces contents of character gmnotes.&lt;/li&gt;`+ ` &lt;li&gt;&lt;code&gt;!set-bio&lt; /code&gt; -- Replaces contents of character bio.&lt;/li&gt;`+ ` &lt;li&gt;&lt;code&gt;!add-gmnote&lt; /code&gt; -- Appends to contents of token gmnotes.&lt;/li&gt;`+ ` &lt;li&gt;&lt;code&gt;!add-cgmnote&lt; /code&gt; -- Appends to contents of character gmnotes.&lt;/li&gt;`+ ` &lt;li&gt;&lt;code&gt;!add-bio&lt; /code&gt; -- Appends to contents of character bio.&lt;/li&gt;`+ ` &lt; /ul&gt;&lt;/div&gt;` ); } } } }); });
Ok so now I see that it is&nbsp; !set-gmnote stabilized and not&nbsp; !set-gmnote -- stabilized but it still crashes my API with the same error message, though the stabilize does show up in the notes.
1625764886
The Aaron
Roll20 Production Team
API Scripter
Your script is the same as mine (aside from a few blank lines).&nbsp; There's no variable named 'n' in it.&nbsp; Try deleting that script tab and re-copying my version.&nbsp; It's possible there's some unicode character in yours that is causing issues.
1625764927
The Aaron
Roll20 Production Team
API Scripter
Also, what other scripts do you have installed.&nbsp; My guess is that something else is taking that input and expecting other things.
tokenmod chatsetattr doorknocker vectormath matrixmath path math alterbars powercards duration (!act) blood&amp;honor
It is loaded in my test game, my actual game has more.
Oh well. I guess I will just do it manually because if we did get this to work on my end I would jack it up by asking to make it useable as @{target|token_name} lol.
1625765978
The Aaron
Roll20 Production Team
API Scripter
That's probably a pretty doable ask. =D
1625766033
timmaugh
Pro
API Scripter
No, you wouldn't. If you have SelectManager installed, you can turn a targeting statement into a selected token by adding the below to your macro command line: {&amp; select @{target|token_id}} That way you don't have to select or deselect any tokens. =D
Well one task at a time. Need to get it working. Now it just crashes and doesn't even add to the gmnotes. Sigh.
So it seems that it doesn't play nice with Blood&amp;Honor (which isn't playing nice with my other macros either, lol). Blood and honor worked fine until I changed the amount of blood and the threshhold for blood loss.&nbsp; Hello square one.
1625770023

Edited 1625770088
timmaugh
Pro
API Scripter
Ok, no, that was my fault. So sorry! I had an inadvertent typo as I copied the code to the message, above... I dropped a rogue 'n' on an empty line. I have updated the Blood&amp;Honor script above to remove that character. Please try it again. Again, apologies!
1625770631
The Aaron
Roll20 Production Team
API Scripter
AH HA!!!&nbsp; =D So, that's cool, that might clear things up completely.
1625771193
timmaugh
Pro
API Scripter
IT problem? I'm on the case.
Thank you all so much. It works. Now for Final Jeopardy. Is the set-gmnotes only useable by GMs or is there a way to allow players to use it?
1625773020
The Aaron
Roll20 Production Team
API Scripter
It is restricted to the GM, but if you remove this from about the 10th line: &amp;&amp; playerIsGM(msg.playerid) They will be able to do it for any token they can select.
Thank you. So it should look like this: if(match ){
1625774548
The Aaron
Roll20 Production Team
API Scripter
yup!
Excellent. Thank you Master Scriptomancer.&nbsp;
I'm back. For some reason stabilized isn't being removed when hit points are reduced. It seemed to be working fine, but not anymore.
1626212542

Edited 1626212637
timmaugh
Pro
API Scripter
Strange. Can you confirm that you have this version installed, still, and that you didn't get some sort of update? Look at lines 108 and 109... they should look like this: 108 109 if (prev[ "bar2_value" ] &gt; obj.get( "bar2_value" )) { obj.set({ gmnotes: obj.get( 'gmnotes' ).replace( /stabilized/gim , '' ) }); That is the bit that handles getting rid of the Stabilized marker if bar2&nbsp; undergoes a change and the new value is less than the previous.
That's exactly how 108 and 109 look.
Any other possible fixes?
1626285165
timmaugh
Pro
API Scripter
I will try to take a look at this later tonight. For now, I can say that the code does require the token to be on the object layer, for the bar2max to NOT be empty, and for the gmnotes on the token to NOT include the text "noblood". Are all of those things true?
Those are all true.
1626461487
timmaugh
Pro
API Scripter
This is working in my game. Can you invite/promote me in your game and I can take a look?
I sent you an invite, as soon as I see you in the game I will promote you. timmaugh said: This is working in my game. Can you invite/promote me in your game and I can take a look?
1626466283
timmaugh
Pro
API Scripter
Me, coming into your game...
1626468410
timmaugh
Pro
API Scripter
OK... so that works in your game, too... ...when I change the bar2 value by hand. Put "stabilized" in a token's GM Notes, make sure there is a Max value to bar2, then enter a lower figure in bar2 than was just there. Check GM Notes. "Stabilized" has disappeared. I'm guessing you're changing that value via a script... which won't dispatch the events that the Blood &amp; Honor script is listening for ("when a token's bar2_value changes, do this thing..."). There are a couple of options to get around that...&nbsp; 1) you can change the script you are using to apply the damage to the token so that IT removes the "Stabilized" text from the GM Notes... but this might be a problem if you have that script from the 1-click and it gets updated by the author. 2) I could write a Plugger plug-in metascript that you could drop into the command line of your existing damage-applying macro. The script would run at meta speed to capture the targeted token's bar2 value as it stands at the start of processing. Then your macro would run. The plugin would launch a delayed (2 seconds) call to validate the same token's bar2 value. If there is a change, it would remove the "stablized" text. Basically, you would have to add something to your existing macro command line like: {&amp; eval}checkstable( &lt;some reference to the token id&gt; ){&amp; /eval} ...where you could replace the reference with a targeting statement, etc. I'm stepping away from my computer, but let me know if you want something like that.
I'm using alterbars
I could do that with a conditional in powercards and have already thought about it. But it would be a lot of work with minimal benefit.&nbsp; The question is, could you add the effect to remove the word stabilized into alterbars? timmaugh said: OK... so that works in your game, too... ...when I change the bar2 value by hand. Put "stabilized" in a token's GM Notes, make sure there is a Max value to bar2, then enter a lower figure in bar2 than was just there. Check GM Notes. "Stabilized" has disappeared. I'm guessing you're changing that value via a script... which won't dispatch the events that the Blood &amp; Honor script is listening for ("when a token's bar2_value changes, do this thing..."). There are a couple of options to get around that...&nbsp; 1) you can change the script you are using to apply the damage to the token so that IT removes the "Stabilized" text from the GM Notes... but this might be a problem if you have that script from the 1-click and it gets updated by the author. 2) I could write a Plugger plug-in metascript that you could drop into the command line of your existing damage-applying macro. The script would run at meta speed to capture the targeted token's bar2 value as it stands at the start of processing. Then your macro would run. The plugin would launch a delayed (2 seconds) call to validate the same token's bar2 value. If there is a change, it would remove the "stablized" text. Basically, you would have to add something to your existing macro command line like: {&amp; eval}checkstable( &lt;some reference to the token id&gt; ){&amp; /eval} ...where you could replace the reference with a targeting statement, etc. I'm stepping away from my computer, but let me know if you want something like that.
Also. I really appreciate the help.
Apparently there is a code fix for alterbars to allow it to work with Blood and Honor. Is there a way to adapt this script to make it remove "stabilized"? // BEGIN INSERTION OF BLOOD AND HONOR FIX if (Target.get("bar3_max") === "" || Target.get("layer") != "objects" || (Target.get("gmnotes")).indexOf("noblood") !== -1) return; // Create spatter near token if "bloodied". // Chance of spatter depends on severity of damage else if (Target.get("bar3_value") &lt;= Target.get("bar3_max") / 2 &amp;&amp; Target.get("bar3_value") &gt; 0) { if (randomInteger(Target.get("bar3_max")) &gt; Target.get("bar3_value")) { BloodAndHonor.createBlood(Target.get("_pageid"), Target.get("left"), Target.get("top"), BloodAndHonor.tokenSize, BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(Target.get("gmnotes"))); } } // Create pool near token if health drops below 1. else if (Target.get("bar3_value") &lt;= 0) { BloodAndHonor.createBlood(Target.get("_pageid"), Target.get("left"), Target.get("top"), Math.floor(BloodAndHonor.tokenSize * 1.5), BloodAndHonor.chooseBlood("pool"), BloodAndHonor.bloodColor(Target.get("gmnotes"))); } // END INSERTION OF BLOOD AND HONOR FIX