![](https://s3.amazonaws.com/files.d20.io/images/247818687/tLf_Clr3lEOW6DSaENjq5Q/thumb.png?1632928045670)
I recently added blood&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. 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: // <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 > 0) { BloodAndHonor.timeout--; } else { return; } } }; fixedCreateObj = (function () { return function () { var obj = createObj.apply(this, arguments); if (obj && !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") <= obj.get("bar2_max") / 3 && prev["bar2_value"] > obj.get("bar2_value") && obj.get("bar2_value") > 0) { if (randomInteger(obj.get("bar2_max")) > 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") <= 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") <= obj.get("bar2_max") / 3 && (obj.get("gmnotes")).indexOf("noblood") == -1) { if (randomInteger(obj.get("bar2_max")) > 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" && msg.content.indexOf("!clearblood") !== -1) { if (BloodAndHonor.useIsGM && !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" && 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(){ 'use strict'; const cmdregex=/^(!(?:set|add)-(?:c?gmnote|bio))\s*/, formatMsg = (c)=>c.replace(cmdregex,'').replace(/(^{{\s*|\s*}}$)/gm,''); on('chat:message',function(msg){ if('api' === msg.type) { let match=msg.content.match(cmdregex); if(match && playerIsGM(msg.playerid) ){ let content = formatMsg(msg.content), who=(getObj('player',msg.playerid)||{get:()=>'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 && 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 => getObj('graphic',s._id)) .reject(_.isUndefined) ; switch(match[1]){ case '!set-gmnote': tokens.each( o => { o.set({ gmnotes: content }); }); break; case '!set-cgmnote': tokens.map( o => getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o => { o.set({ gmnotes: content }); }); break; case '!set-bio': tokens.map( o => getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o => { o.set({ bio: content }); }); break; case '!add-gmnote': tokens.each( o =>{ o.set({ gmnotes: `${o.get('gmnotes')}<br>\n${content}` }); }); break; case '!add-cgmnote': tokens.map( o => getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o => { o.get('gmnotes',(notes)=>{ _.defer(()=>o.set({ gmnotes: `${notes}<br>\n${content}` })); }); }); break; case '!add-bio': tokens.map( o => getObj('character',o.get('represents'))) .reject(_.isUndefined) .each( o => { o.get('bio',(notes)=>{ _.defer(()=>o.set({ bio: `${notes}<br>\n${content}` })); }); }); break; } } else { sendChat('',`/w "${who}" <div>`+ `<div>Use <code>COMMNAD Some Text</code> or <code>COMMAND {{ some multi-line text}}</code>.</div>`+ `<div><b>Commands:</b></div>`+ `<div><ul>`+ `<li><code>!set-gmnote</code> -- Replaces contents of token gmnotes.</li>`+ `<li><code>!set-cgmnote</code> -- Replaces contents of character gmnotes.</li>`+ `<li><code>!set-bio</code> -- Replaces contents of character bio.</li>`+ `<li><code>!add-gmnote</code> -- Appends to contents of token gmnotes.</li>`+ `<li><code>!add-cgmnote</code> -- Appends to contents of character gmnotes.</li>`+ `<li><code>!add-bio</code> -- Appends to contents of character bio.</li>`+ `</ul></div>` ); } } } }); }); 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.